Как новые разработчики программного обеспечения, использование недавно изученных инженерных инструментов для создания собственных приложений - одна из самых захватывающих вещей, которые нам приходится делать. Иногда в этих проектах участвуют несколько пользователей, взаимодействующих друг с другом в режиме реального времени (например, в многопользовательской игре или приложении чата). В ситуациях, когда мы хотим, чтобы пользователи (клиенты) получали обновления от наших приложений (сервера) в режиме реального времени без необходимости инициировать запрос, протокол WebSockets может быть лучший вариант, чем типичный протокол HTTP-запроса-ответа.
В этом руководстве объясняется, как создать простое приложение для обмена сообщениями, используя:
- Внутренний api Rails с использованием Rails Action Cable для обработки соединений WebSocket
- Одностраничный интерфейс приложения, использующий только ванильный Javascript
Ссылки на пример кода на Github:
Оглавление
Часть 1: Настройка обычного серверного API Rails
Часть 2: Настройка простого одностраничного интерфейсного приложения
Часть 3: Добавление Action Cable в нашу серверную часть Rails
Часть 5: Протестируйте свое новое приложение для обмена сообщениями!
Часть 1: Настройка обычного серверного API Rails
1. Создайте новое приложение rails с серверной частью PostgreSQL в конфигурации api (имя приложения: test_messaging_app)
$ rails new test_messaging_app_back_end --database=postgresql --api
2. Создайте базу данных
$ rails db:create
МОМЕНТ ТЕСТИРОВАНИЯ:
Убедитесь, что ваше приложение создано правильно:
- Запустите свой сервер rails
$ rails server
- Посетите http: // localhost: 3000 в своем браузере, чтобы убедиться, что приложение было создано правильно. Вы должны увидеть стандартное приветственное сообщение Rails (Ура! You’re on Rails!)
3. Создайте две модели: Сообщение и Комната чата.
Используя генераторы рельсов, мы можем одновременно создать следующее:
I. Файл модели в каталоге / app / models /
II. Файл миграции с отметкой времени в каталоге / db / migrate
$ rails generate model Message content:string chat_room_id:integer $ rails generate model ChatRoom name:string
После создания наших моделей и файлов миграции мы можем создавать таблицы в нашей базе данных.
$ rails db:migrate
МОМЕНТ ТЕСТИРОВАНИЯ:
Проверьте свой файл /db/schema.rb, чтобы убедиться, что база данных была создана правильно.
Наконец, давайте настроим активные ассоциации моделей записей в наших файлах моделей.
// test_messaging_app_back_end/app/models/chat_room.rb class ChatRoom < ApplicationRecord has_many :messages end // test_messaging_app_back_end/app/models/message.rb class Message < ApplicationRecord belongs_to :chat_room end
4. Создайте маршруты для каждого из ресурсов.
// test_messaging_app_back_end/config/routes.rb Rails.application.routes.draw do resources :chat_rooms, only: [:index, :create, :show] resources :messages, only: [:create] end
5. Создайте контроллеры для каждого из ресурсов.
Мы снова можем использовать генераторы рельсов, чтобы быстро создавать файлы наших контроллеров.
$ rails generate controller ChatRooms $ rails generate controller Messages
После этого нам нужно будет создать определенные методы, соответствующие нашим маршрутам RESTful.
// test_messaging_app_back_end/app/controllers/chat_rooms_controller.rb class ChatRoomsController < ApplicationController def index chat_rooms = ChatRoom.all render json: chat_rooms end def create chat_room = ChatRoom.new(chat_room_params) if chat_room.save render json: chat_room else render json: {errors: game.errors.full_messages}, status: 422 end end def show chat_room = ChatRoom.find(params[:id]) render json: chat_room, include: [:messages] end private def chat_room_params params.require(:chat_room).permit(:name) end end // test_messaging_app_back_end/app/controllers/messages_controller.rb class MessagesController < ApplicationController def create message = Message.new(message_params) if message.save render json: message else render json: {errors: message.errors.full_messages}, status: 422 end end private def message_params params.require(:message).permit(:content, :chat_room_id) end end
МОМЕНТ ТЕСТИРОВАНИЯ:
Убедитесь, что ваши маршруты правильно совпадают с правильными действиями контроллера.
$ rails routes
Вы также должны создать несколько примеров данных (вручную в консоли Rails или путем заполнения файла /db/seeds.rb) и просмотреть визуализированный json по адресу http: // localhost: 3000 / chat_rooms /
6. Настройте политику CORS, чтобы разрешить доступ из нашего внешнего приложения.
Я. Раскомментируйте gem ‘rack-cors’ из Gemfile.
II. Запустите bundle install, чтобы установить гем "rack-cors".
III. Раскомментируйте следующий код
// test_messaging_app_back_end/config/initializers/cors.rb Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'example.com' resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end end
Измените "example.com" на "*", чтобы разрешить запросы со всех исходных URL.
Часть 2: Настройка простого одностраничного интерфейсного приложения
1. Создайте папку для своего интерфейсного приложения.
$ mkdir test_messaging_app_front_end
2. Создайте в этой папке два файла:
I. index.html → Этот файл будет содержать нашу исходную структуру html, включая:
ПРИМЕЧАНИЕ: ССЫЛКА НА ОБРАЗЕЦ КОДА GITHUB
Chat Rooms <div> - New Chat Room <form> - Chat Rooms List <div> Chat Room Div - New Message <form> - Messages List <div>
II. index.js → Этот файл будет содержать весь наш ванильный Javascript, что позволит нам:
ПРИМЕЧАНИЕ: ССЫЛКА НА ОБРАЗЕЦ КОДА GITHUB
- Визуализировать все чаты с сервера при начальной загрузке страницы
- Добавьте слушателей событий для: (1) создания комнаты чата, (2) присоединения к комнате чата, (3) создания новых сообщений в комнате чата.
Часть 3: Добавление Action Cable в нашу серверную часть Rails
1. Настройте URL-адреса WebSocket.
Наши кабели будут отправляться на определенные URL-адреса и с них, добавив в наш файл маршрутов следующее:
// test_messaging_app_back_end/config/routes.rb Rails.application.routes.draw do resources :chat_rooms, only: [:index, :create, :show] resources :messages, only: [:create] mount ActionCable.server => '/cable' end
2. Создайте наш вещательный канал
Снова используя генераторы рельсов, мы создадим новый канал ChatRoomChannel.
$ rails generate channel ChatRoom
3. Укажите, откуда мы хотим вести трансляцию.
Во вновь созданном ChatRoomChannel мы назначим конкретные экземпляры комнаты чата, откуда мы будем вести трансляцию.
ПРИМЕЧАНИЕ. В этом примере мы будем транслировать новые сообщения только в конкретный поток комнаты чата, в котором оно было создано.
// test_messaging_app_back_end/app/channels/chat_room_channel.rb class ChatRoomChannel < ApplicationCable::Channel def subscribed chat_room = ChatRoom.find(params[:id]) stream_for chat_room end def unsubscribed # Any cleanup needed when channel is unsubscribed end end
4. Обновите наш контроллер сообщений, чтобы транслировать новые сообщения.
В контроллере сообщений настройте метод create для трансляции нового сообщения в потоке комнаты чата этого сообщения в канале комнаты чата. Также закомментируйте код, который отображает сообщение через ответ json из нашего запроса на выборку. Наше интерфейсное приложение будет отображать только новые сообщения на основе сообщений, поступающих через соединение WebSocket, поэтому мы НЕ выполняем двойную визуализацию сообщений, которые мы создаем на странице.
// test_messaging_app_back_end/app/controllers/messages_controller.rb class MessagesController < ApplicationController def create message = Message.new(message_params) if message.save chat_room = ChatRoom.find(message.chat_room_id) ChatRoomChannel.broadcast_to(chat_room, message) # render json: message else render json: {errors: message.errors.full_messages}, status: 422 end end private def message_params params.require(:message).permit(:content, :chat_room_id) end end
5. Разрешите определенным источникам запросов устанавливать новые соединения WebSocket.
Подобно обновлению нашей CORS ранее, нам нужно будет явно разрешить соединения WebSocket из определенных источников, добавив следующее:
Примечание: настройте ‘file: //’ на то, откуда открывается ваше интерфейсное приложение.
// test_messaging_app_back_end/config/environments/development.rb config.action_cable.allowed_request_origins = [ # Local address we use for our standalone client 'file://', ]
Часть 4: Добавление подключений WebSocket к нашему клиентскому приложению с использованием Vanilla Javascript
1. Создайте функцию, которая открывает соединение WebSocket с нашей серверной частью Rails.
Эта функция будет иметь несколько основных компонентов, описанных в комментариях ниже:
// test_messaging_app_front_end/index.js // Opens a WebSocket connection to a specific Chat Room stream. function createChatRoomWebsocketConnection(chatRoomId) { // Creates the new WebSocket connection. socket = new WebSocket(webSocketUrl); // When the connection is first created, this code runs subscribing the client to a specific chatroom stream in the ChatRoomChannel. socket.onopen = function(event) { console.log('WebSocket is connected.'); const msg = { command: 'subscribe', identifier: JSON.stringify({ id: chatRoomId, channel: 'ChatRoomChannel' }), }; socket.send(JSON.stringify(msg)); }; // When the connection is closed, this code will run. socket.onclose = function(event) { console.log('WebSocket is closed.'); }; // When a message is received through the websocket, this code will run. socket.onmessage = function(event) { const response = event.data; const msg = JSON.parse(response); // Ignores pings. if (msg.type === "ping") { return; } console.log("FROM RAILS: ", msg); // Renders any newly created messages onto the page. if (msg.message) { renderMessage(msg.message) } }; // When an error occurs through the websocket connection, this code is run printing the error message. socket.onerror = function(error) { console.log('WebSocket Error: ' + error); }; }
2. Обновите прослушиватель событий, используемый для отправки новых сообщений, чтобы он больше не отображал новые сообщения на основе ответа json-выборки с сервера.
Поскольку теперь мы будем получать сообщения с сервера при создании ЛЮБОГО сообщения (даже если оно создано не нами), мы не хотим дублировать отправленные сообщения.
// test_messaging_app_front_end/index.js // Allow users to submit new messages newMessageForm.addEventListener('submit', event => { event.preventDefault(); fetch(`${apiUrl}/messages`,{ method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ content: event.target[0].value, chat_room_id: event.target.dataset.chatRoomId }) }) // .then(response => response.json()) // .then(messageObject => {renderMessage(messageObject)}) newMessageForm.reset(); })
Часть 5. Протестируйте свое новое приложение для обмена сообщениями!
- Из вашего внутреннего приложения запустите свой сервер rails
$ rails server
2. В интерфейсном приложении откройте файл index.html в обычном браузере.
$ open index.html
3. Откройте один и тот же файл index.html в частном окне в режиме инкогнито (Google Chrome), чтобы смоделировать 2 разных клиента.
Благодаря этому вы сможете:
- Создавайте новые чаты
- Присоединяйтесь к определенным чат-комнатам
- Отправлять сообщения в определенной чат-комнате
- Получайте сообщения, сделанные в определенной комнате чата другими клиентами в режиме реального времени!
Ресурсы
- Обзор Action Cable (Руководства по Rails)
- Написание клиентских приложений WebSocket - MDN Web Docs
- Разговор с ActionCable без рельсов
- Как работает JavaScript: глубокое погружение в WebSockets и HTTP / 2 с SSE + как выбрать правильный путь
- Rails в реальном времени: реализация WebSockets в Rails 5 с помощью Action Cable
- Более удобное использование Action Cable
- Подключите автономное JS-приложение к ActionCable - добро в реальном времени