Фон

На последней встрече ReactNYC Meetup (да, той самой, на которую я ссылался в своем предыдущем блоге) мне была представлена ​​новая технология: Mirage JS.

Mirage JS - это имитирующая библиотека API, которая позволяет создавать, тестировать и публиковать полное работающее приложение JavaScript, не полагаясь на какие-либо серверные службы.

Mirage JS выполняет это путем создания фиктивного сервера и перехватывает любые сетевые запросы к фиктивному серверу вместо официального. Он создает фиктивный сервер в браузере. Сэм Селикофф сделал вводную презентацию о том, как быстро запустить приложение React с помощью имитационного сервера через Mirage JS. Он представил Mirage JS, используя соглашения RESTful для фиктивного сервера и фиктивную базу данных для сопровождения этих соглашений.

Я немного изучил Mirage JS и взглянул на страницу Github, на сайте которой была их демонстрация. Опять же, это были все маршруты и соглашения RESTful, как в презентации Сэма. Именно тогда я решил поэкспериментировать с Mirage JS и посмотреть, сможет ли он выйти за рамки того, что я читал и видел до сих пор.

Локальное хранилище с Mirage JS

Маршруты, противоположные RESTful, будут настраиваемыми маршрутами, поэтому первая идея настраиваемых маршрутов, которая пришла в голову, заключалась в проверке подлинности через локальное хранилище. Поскольку Mirage JS создает фиктивный сервер, шаблон MVC (модель-представление-контроллер) все еще может быть реализован. Я пропущу часть модели для моего примера локального хранилища, поскольку модель поможет только подтвердить декодированную полезную нагрузку локального хранилища по сравнению с пользовательской моделью (что можно сделать с помощью schema.users.findBy({ ATTRIBUTE TO FIND BY }).

В примере с локальным хранилищем я буду использовать JWT (JSON Web Token), который закодирован и хранится в локальном хранилище. Если вы не знакомы с JWT, я настоятельно рекомендую прочитать об этом на их сайте, чтобы ознакомиться с ним, прежде чем читать этот блог дальше.

Примечание. Не ссылайтесь на этот блог как на справочник по использованию JWT для ваших реализаций безопасности. Этот блог предназначен только для демонстрации того, что Mirage JS также можно использовать для настраиваемых маршрутов.

Теперь, когда идея появилась, приступим к ее реализации!

Установка Mirage JS и JWT

Прежде чем мы сможем начать использовать инструменты, нам потребуются сами инструменты. Я следовал Руководству по быстрому запуску Mirage JS для целей установки и документации JWT. Две приведенные ниже команды установят эти пакеты.

Для Mirage JS:

npm install --save-dev miragejs

Для JWT:

npm install --save jsonwebtoken

Мок-сервер Mirage JS

Теперь, когда у нас есть инструменты, давайте приступим к их внедрению. Ниже представлена ​​моя первоначальная установка макетного сервера:

Этот фиктивный сервер довольно прост. Все, что делает функция, - это запускает новый сервер (строка 4) в среде разработки (строка 3) и создает пространство имен для моих маршрутов, что означает, что каждый маршрут, который мой фиктивный сервер пытается проверить, должен начинаться с «/ api». . Это похоже на сервер, показанный в кратком руководстве, за исключением того, что я удалил семена, модели и маршрут пользователя, поскольку я не использую никаких моделей.

Дополнительный код будет добавлен по мере необходимости в этот блог. Перейдем к интерфейсу.

App.js

Приведенная ниже суть - это отправная точка, в которой код становится более интересным.

В этом смысле я использую ловушку React useEffect(), чтобы немедленно запустить запрос на выборку, как только компонент будет смонтирован.

Обычно для аутентификации, как только пользователь предоставляет какие-то учетные данные и он действительно аутентифицируется, выдается JWT. В этом примере я пропускаю эти шаги и сразу запускаю JWT, как только компонент монтируется.

В моем запросе на выборку я делаю запрос GET для маршрута «/ api / auth», которого у меня в настоящее время нет на фиктивном сервере. Итак ... давайте построим это!

Создание и декодирование JWT

Давайте добавим маршрут «/ api / auth» в фиктивный сервер.

В Mirage JS this.get, this.post, this.patch, и this.del обычно являются синтаксисом фиктивных маршрутов. Это основано на HTTP-глаголах, которые легко запомнить.

Согласно документации Mirage JS,

Каждый метод глагола имеет одинаковую подпись. Первый аргумент - это путь (URL), а второй - фактический обработчик функции, возвращающий ответ.

Я знаю, что в этом случае я хочу создать закодированный JWT, поэтому тело обработчика функции должно выполнить это.

Посмотрев документацию JWT, я могу сделать это с помощью метода sign.

В строках 12–14 я создал новый JWT, в котором полезная нагрузка { message: "YOU'VE DECODED ME!" } с secretCode. secretCode - это сгенерированный код с веб-сайта JWT, в котором я сохранил его в отдельном файле с именем secret.js. Теперь у меня есть ответ на запрос на выборку.

Давайте сохраним закодированный токен в локальном хранилище и изменим состояние для отображения статуса токена.

В строках 8–14 я устанавливаю элемент в локальном хранилище с парой ключ-значение токена и зашифрованным кодом. Затем я устанавливаю состояние токена, чтобы показать в браузере, что сообщение все еще закодировано. Ниже приведен скриншот того, как это выглядит.

Если вам интересно, почему мне пришлось заключить настройку веб-токена в оператор if, я объясню это позже в этом блоге.

Большой! Теперь, когда мы создали закодированный JWT, давайте его расшифруем. Чтобы вызвать это событие, я собираюсь добавить кнопку, которая будет запускать функцию декодирования и маршрут, по которому она будет пинговать.

Я добавил кнопку в строке 29 и создал функцию декодирования в строках 17–24. Теперь, поскольку у меня еще нет маршрута декодирования, давайте создадим его, точно так же, как мы это делали для маршрута аутентификации. Я также отправляю закодированный токен из локального хранилища через заголовки в запросе на выборку, чтобы я мог отправить его на свой фиктивный сервер (или иначе, как я должен его декодировать в противном случае?).

Хммм, теперь мне нужно расшифровать закодированный токен. Но для этого мне нужно получить закодированный токен из заголовков.

Согласно документации Mirage JS:

Второй параметр - это объект request, который содержит информацию о запросе, сделанном вашим приложением. Например, из него можно получить доступ к динамическим сегментам URL:

После кропотливого просмотра всех пар "ключ-значение" и нескольких поисков в Google способ получить закодированный токен - request.requestHeaders.authorization.

Милая, теперь, когда у меня есть закодированный токен, мне нужно его декодировать. К счастью, документация JWT проста, и я могу использовать метод verify для его декодирования с тем же secretCode, который я использовал для его кодирования.

Строки 18–22 - это обработчик функции, который позволяет мне декодировать закодированный токен и возвращать декодированный токен. Однако в строке 18 мне пришлось заполнить первый аргумент schema, а затем второй аргумент request, представляющий интерес аргумент. Поскольку в этом примере мы не используем первый аргумент, я попытался заменить его на null, но получал ошибки. Согласно соглашениям Mirage JS, я назвал первый аргумент schema, чтобы обойти указанные ошибки.

Теперь, когда мы возвращаем данные из маршрута декодирования, давайте завершим функцию декодирования.

В строках 24–28 я получил декодированный токен от фиктивного сервера, а затем установил состояние токена на декодированный токен, чтобы отобразить его скрытое сообщение. В качестве быстрой проверки работоспособности я также записал данные в журнал, чтобы убедиться, что исходная полезная нагрузка была такой же, как и раньше. Для ключа iat:

Сгенерированные jwts по умолчанию будут включать претензию iat (выданная), если не указано noTimestamp.

Поскольку ключ iat генерируется автоматически, и я не управляю этой парой ключ-значение данных, я могу спокойно игнорировать его в этом примере.

useEffect ()

Вспомните, как я упоминал ранее, что мне нужно было использовать оператор if в ловушке useEffect()? Это связано с неявной природой этого крючка. Согласно документации React hooks:

Совет

Если вы знакомы с методами жизненного цикла класса React, вы можете думать о useEffect Hook как о componentDidMount, componentDidUpdate и componentWillUnmount вместе взятых.

По сути, то, как я использую useEffect(), я удваиваю его как componentDidMount и componentDidUpdate. Аспект componentDidMount - это запрос выборки маршрута аутентификации, тогда как аспект componentDidUpdate - запрос выборки маршрута декодирования.

Без оператора if следующий логический поток состояния token был бы следующим:

  1. токен: «ЗАКОДИРОВАННОЕ СЕКРЕТНОЕ СООБЩЕНИЕ» (когда компонент установлен)
  2. Нажмите на кнопку, чтобы расшифровать закодированный токен
  3. токен: «ВЫ МЕНЯ ДЕКОДИРОВАЛ» (с момента обновления токена в функции decode)
  4. токен: «ЗАКОДИРОВАННОЕ СЕКРЕТНОЕ СООБЩЕНИЕ» (в хуке useEffect, так как состояние токена было обновлено)

Я разместил несколько console.log, чтобы продемонстрировать этот поток, и скриншот ниже это доказывает.

Добавляя оператор if, он не позволяет аспекту componentDidUpdate обновлять состояние токена и сбрасывать его обратно в «ЗАКОДИРОВАННОЕ СЕКРЕТНОЕ СООБЩЕНИЕ». Ниже приведен снимок экрана с console.logs, чтобы продемонстрировать это.

Однако вы заметите, что после функции decode в консоли браузер по-прежнему делает еще один запрос GET для маршрута аутентификации, но не обновляет состояние токена. Это связано с тем, что, когда состояние токена обновляется в функции decode, запускается аспект componentDidUpdate, который запускает ловушку useEffect(). Из-за расположения оператора if в коде он не препятствует доступу кода к маршруту, хотя и предотвращает изменение состояния токена. Это означает, что при каждом нажатии кнопки код будет обращаться к маршруту аутентификации и каждый раз создавать новый JWT. Это ужасная практика, и ее никогда нельзя применять таким образом. Поскольку этот блог посвящен настраиваемым маршрутам, мы пока можем пропустить этот вопрос.

Ключевые выводы

Во время работы с Mirage JS и создания этого примера я узнал, насколько мощным может быть Mirage JS. Ниже были мои открытия:

  1. Обработчики функций в макетных маршрутах сервера, такие как this.gets(), по сути такие же, как контроллер в шаблоне MVC. То, как обработчики функций обрабатывают данные в каждом из своих маршрутов, аналогично классу Controller в Ruby on Rails.
  2. Хук useEffect() сочетает в себе функции componentDidMount, componentDidUpdate и componentWillUnmount. Различение этих двух функций в одном и том же хуке может оказаться трудным (возможно, исследуйте и прочтите более подробно документацию по хукам React).
  3. Это открытие, наряду с моим первым, помогло мне понять, насколько мощным может быть Mirage JS. Как разработчик внешнего интерфейса / полного стека, Mirage JS может смоделировать всю вашу базу данных и внутреннюю архитектуру перед разработкой самого внутреннего интерфейса. На основе использования шаблона MVC модели находятся на макетном сервере, RESTful и настраиваемые маршруты могут быть созданы / протестированы, контроллеры находятся в обработчиках функций, а представления находятся в самом внешнем коде. Главное, что осталось сделать, - это перенести часть вашего кода Mirage JS с языка Javascript на выбранный вами внутренний язык, отсоединить внешний код от макетного сервера и подключить внешний код к фактический сервер. И тогда, БАМ, у вас есть полностью работающее приложение, готовое к производству!