ApiBlaze – это инструмент для изучения спецификаций API: поиск по ключевому слову, фильтрация объектов, свойств или конечных точек, а также просмотр описаний и примеров кода. ApiBlaze поможет вам молниеносно ответить на конкретный вопрос об API. Вы можете попробовать это здесь: apblaze.admantium.com.

В моих предыдущих статьях я рассказал, почему веб-сокеты являются важным методом для ApiBlaze: они обеспечивают длительные полнодуплексные соединения между двумя серверами для непрерывной потоковой передачи данных. Поскольку спецификация открытого API довольно велика (полная спецификация Kubernetes занимает 4 МБ текста), поиск этих данных, их преобразование и отправка обратно в браузер требует довольно больших вычислительных ресурсов. В ApiBlaze эти действия выполняются на стороне сервера, а затем отправляются обратно в браузер для немедленного рендеринга.

Это соответствует следующему основному требованию:

  • TECH03 — Используйте веб-сокеты для подключения внешнего и внутреннего интерфейса

В этой статье подробно рассказывается, как создать сборку внутреннего сервера с помощью экспресс-фреймворка и библиотеки socket.io, а также как подключить ее к клиентскому интерфейсу, работающему в браузере. Хотя шаги объясняются в контексте ApiBlaze, они являются общими, и вы можете применить их для любой внешней библиотеки, такой как приложения React, Vue или PlainJS.

Эта статья первоначально появилась в разделе мой блог.

Бэкенд-сервер

Бэкэнд основан на express и socket.io (v2.3)1. Давайте установим пакеты.

npm i -S express [email protected]

После завершения установки мы создадим 4 файла, чтобы разделить внутренний код на разные области ответственности.

  • index.js: реализует экземпляр экспресс-сервера, открывая порт, через который можно получить доступ к соединению через веб-сокет.
  • connect.js: создает экземпляр веб-сокета, получая экземпляр узла HttpServer, объект, который, например. создано с экспресс
  • handler.js: обработчик определяет все события веб-сокета, к которым и определяет, как они обрабатываются.
  • actions.js: Содержит конкретные функции, которые будут вызываться при получении зарегистрированного события, и они возвращают свои результаты обработчику, который, в свою очередь, возвращает их вызывающей стороне.

Этот простой макет помогает четко отделить код серверного приложения. Давайте теперь подробно рассмотрим содержимое этих файлов.

Веб-сервер с конечной точкой WebSocket

Веб-сервер express определен в index.js.

//*  index.js *//
const express = require('express')
const websocketConnection = require('./connect.js')
app = express()
const httpServer = app.listen(3000, () => {
  console.log(`BOOTING | api-blaze-backend v0.0.1`)
})
websocketConnection(httpServer)

В этих нескольких строках кода 2 мы создаем экземпляр экспресс-сервера для прослушивания порта 3000 (строка 7), а затем передаем этот экземпляр функции websocketConnection (строка 11).

Соединитель

Соединитель определяет, как настроен веб-сокет. Мы создаем экземпляр с именем io (строка 6), который получает экземпляр экспресс-сервера и необязательный объект конфигурации. Вариантов множество, см. официальную документацию. Затем для экземпляра веб-сокета мы определяем прослушиватель событий для события connection (строка 9). Когда это событие произойдет, обработчик возьмет на себя управление.

//*  connect.js *//
const websocket = require('socket.io')
const handleConnection = require('./handler.js')
function websocketConnection (httpServer) {
  const io = websocket(httpServer, {
    serveClient: false
  })
  io.on('connection', socket => handleConnection(socket))
}
module.exports = websocketConnection

Обработчик и действия

В файле handler.js мы определяем, какие сообщения обрабатывает веб-сокет и как на них реагировать. События определяются с помощью метода io.on, который получает имя события, его аргументы и функцию обратного вызова, которая будет выполнена. В строке 6 мы определяем, как обрабатывать сообщение system:healthcheck: мы будем регистрировать полученное сообщение, а затем emit ответ с сообщением healthy. Точно так же в строке 10 мы определяем обработку сообщения app:api-search-action, которое выполнит функцию действия apiSearchAction.

//*  handler.js *//
const { apiSearchAction } = require('./actions')
const clients = {}
function handleConnection (socket) {
  console.log(`+ client ${socket.id} has connected`)
  clients[socket.id] = { connected: true }
  socket.on('system:healthcheck', msg => {
    console.log(msg)
    socket.emit('system:healthcheck', 'healthy')
  })
  socket.on('app:api-search-action', keyword => {
    console.log('app:api-search-action', keyword)
    socket.emit('app:api-search-action', apiSearchAction(keyword))
  })
}
module.exports = handleConnection

Действия — это простые функции JavaScript. apiSearchAction загрузит инвентаризацию API, статический файл, содержащий имя, описание и ссылку на файл на стороне сервера для файла спецификации API. Он будет искать ключевое слово в этом представлении, преобразовывая ключевое слово в регулярное выражение, а затем ранжируя все API по количеству совпадений этого ключевого слова.

Пример действия:

//*  action.js *//
const apiInventory = require('./spec/inventory.json')
function apiSearchAction (keyword) {
  const regex = new RegExp(keyword, 'ig')
  var res = []
  for (let [name, definition] of Object.entries(apiInventory)) {
    const occurences = JSON.stringify(definition).match(regex)
    const score = (occurences && occurences.length) || 0
    res.push({ name, score, definition })
  }
  return res.sort((a, b) => b.score - a.score)
}

Теперь мы объяснили внутреннюю обработку поиска. Давайте посмотрим, как это соединение устанавливается и обрабатывается во внешнем интерфейсе.

Подключение внешнего интерфейса

Интерфейс предлагает два разных варианта установки socket.io. Вы можете добавить тег <script> вручную, ссылаясь на статически предоставленный файл JavaScript socket.io, или вы можете использовать сборщик, такой как сборщик Snowpack, который автоматически установит библиотеку.

Чтобы настроить snowpack и клиентскую библиотеку socket.io, выполните следующую команду:

npm i -s snowpack [email protected]

После завершения определите файл connect.js, который создаст экземпляр веб-сокета, который подключается к внутреннему серверу.

//*  connect.js (Frontend) *//
import io from 'socket.io-client'
export default io('ws://127.0.0.1:8081', { cookie: false })

Затем вы можете импортировать экземпляр websocket в другие файлы, например, в index.js, и начать с отправки сообщения проверки работоспособности. Будьте осторожны: при импорте socket.io-client будет определен ряд объектов в области global, таких как Server, Socket, Client — не используйте объекты с похожими именами в своем приложении.

import websocket from './globals/connect.js'
function init () {
  websocket.emit('system:healthcheck', 'ok?')
  websocket.on('system:healthcheck', msg => {
    console.log(msg)
  })
}
init()

Этот экземпляр веб-сокета работает аналогично тому, что мы уже видели в бэкэнде. С помощью websocket.emit сообщения отправляются, а с помощью websocket.on определяются обработчики входящих сообщений.

Экземпляр, экспортированный в connect.js, можно использовать и в других классах. Вот пример для SearchApiAction — он выдаст сообщение app:api-search-action и, получив ответ, передаст результаты функции обратного вызова.

import websocket from '../globals/connect.js'
export default class SearchApiAction extends Action {
  action (cb) {
    websocket.emit('app:api-search-action', 'dummy')
    websocket.on('app:api-search-action', json => {
      cb(json)
    })
  }
}

Обзор: требования проекта ApiBlaze

После завершения рефакторинга мы имеем следующий статус с требованиями ApiBlaze:

Поиск APIS

  • ✅ SEA01 — Поиск API по ключевому слову
  • ✅ SEA02 — Показать результаты поиска во всплывающем окне
  • ✅ SEA03 — Выберите результаты поиска с помощью клавиш со стрелками, введите и щелкните мышью

Рамки

  • ✅ FRAME01 — Контроллер и маршрутизация
  • ✅ FRAME02 — Страницы и компоненты с отслеживанием состояния
  • ✅ FRAME03 — Действия
  • ✅ FRAME04 — Оптимизированная комплектация

Технологии

  • ✅ TECH01 — Используйте PlainJS и пользовательский фреймворк
  • ✅ TECH02 — Используйте SAAS для CSS
  • ✅ TECH03 — Используйте веб-сокеты для подключения внешнего и внутреннего интерфейса

Вывод

Использование WebSockets для подключения серверной части к интерфейсной позволяет формировать долговременные полнодуплексные соединения для непрерывной потоковой передачи данных. Для серверной части основными шагами являются: Импортируйте библиотеку socket.io, создайте экземпляр узла HttpServer и используйте этот экземпляр для создания экземпляра Socket.IO. Затем вы определяете прослушиватели событий с помощью методов io.on и io.emit. Клиенту необходимо импортировать клиентскую библиотеку socket.io, создать экземпляр, который подключается к серверной части, а также использовать io.on и io.emit для определения и обработки сообщений, которыми будет осуществляться обмен. Попробуйте использовать WebSockets в одном из ваших проектов — они мощные и простые в настройке.

Сноски

  1. На момент написания был выпущен websocket.io v 3.0, но я не смог заставить его работать и вместо него выбрал более старую версию v2.3.
  2. В этом примере экспресс-конфигурация довольно проста, но вы можете добавить любой другой экспресс-модуль, например. для обработки статических файлов или для установки значений CORS.