Как новые разработчики программного обеспечения, использование недавно изученных инженерных инструментов для создания собственных приложений - одна из самых захватывающих вещей, которые нам приходится делать. Иногда в этих проектах участвуют несколько пользователей, взаимодействующих друг с другом в режиме реального времени (например, в многопользовательской игре или приложении чата). В ситуациях, когда мы хотим, чтобы пользователи (клиенты) получали обновления от наших приложений (сервера) в режиме реального времени без необходимости инициировать запрос, протокол WebSockets может быть лучший вариант, чем типичный протокол HTTP-запроса-ответа.

В этом руководстве объясняется, как создать простое приложение для обмена сообщениями, используя:

  • Внутренний api Rails с использованием Rails Action Cable для обработки соединений WebSocket
  • Одностраничный интерфейс приложения, использующий только ванильный Javascript

Ссылки на пример кода на Github:

Оглавление

Часть 1: Настройка обычного серверного API Rails

Часть 2: Настройка простого одностраничного интерфейсного приложения

Часть 3: Добавление Action Cable в нашу серверную часть Rails

Часть 4: Добавление подключений WebSocket к нашему интерфейсному приложению с использованием Vanilla Javascript

Часть 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. Протестируйте свое новое приложение для обмена сообщениями!

  1. Из вашего внутреннего приложения запустите свой сервер rails
$ rails server

2. В интерфейсном приложении откройте файл index.html в обычном браузере.

$ open index.html

3. Откройте один и тот же файл index.html в частном окне в режиме инкогнито (Google Chrome), чтобы смоделировать 2 разных клиента.

Благодаря этому вы сможете:

  1. Создавайте новые чаты
  2. Присоединяйтесь к определенным чат-комнатам
  3. Отправлять сообщения в определенной чат-комнате
  4. Получайте сообщения, сделанные в определенной комнате чата другими клиентами в режиме реального времени!

Ресурсы