Заглушки для HTTP-сервера в AngularJS-приложении

Сегодня мы поговорим про заглушки в AngularJS-приложениях. Именно в приложениях, а не в тестах! Очень часто при разработке клиентской части приложения приходится реализовывать новую функциональность, не имея при этом рабочей функциональности на серверной стороне. А ещё иногда возникает потребность показать заказчику прототип приложения, не реализовывая при этом API для экономии времени. Когда-то я делал для этих целей на стороне HTTP-сервера (обычно на уровне nginx) заглушки — статические JSON-файлы, которые при подходящих location’ах отдавались наружу. Структура JSON’а в таком файле совпадает со спецификацией API, поэтому клиентское приложение можно разрабатывать со всеми HTTP-вызовами, не задумываясь особо о том, что сервер частично искусственный.

Но в этом подходе всё не так безоблачно! При разнообразии операций над REST-ресурсами (хотя не обязательно REST) в описании location’ов nginx’а могут появляться нетривиальные условия, вычисление статуса ответа (к примеру, при добавлении через POST-запрос REST API обычно отвечает HTTP-статусом 201, а при получении списка 200), HTTP-метода и т.д.. Реализация такой логики иногда занимает больше времени, чем хотелось бы. И тут нам на помощь приходит AngularJS-модуль ngMockE2E с сервисом $httpBackend, о котором многие разработчики несправедливо забывают или вовсе не знают.

Недавно я уже писал про нюансы при работе с $httpBackend в ngMock, но та заметка касалась unit-тестирования AngularJS-приложений. А вот $httpBackend в ngMockE2E позволяет нам настроить описания бэкенда («Backend Definitions») через when-методы и использовать их при работе с нашим приложением. Вполне логично, что в отличие от ngMock, у него отсутствуют методы по созданию ожиданий запросов («Request Expectations») через expect-методы, т.к. таких методов у сервиса $httpBackend в модуле ngMockE2E просто нет в связи с отсутствием необходимости верификации.

Так вот, для настройки ответов сервера при разработке или прототипировании (!!!) web-приложения, можно сделать такой трюк:

  1. Подключить к нашей странице файл angular-mocks.js, в котором определены модули ngMock и ngMockE2E;
  2. Определить модуль, который будем использовать только в среде разработки, в котором будут определяться заглушки ответов сервера. Он может выглядеть примерно так:
    // Объявляем новым модуль с зависимостью ngMockE2E. При старте модуля делаем инъекцию зависимости $httpBackend.
    angular.module('development', ['ngMockE2E']).run(['$httpBackend', function ($httpBackend) {
    	// Настраиваем заглушку на все GET-запросы получения списка пользователей с какими-нибудь критериями фильтрации. Определяем HTTP-код статуса и статический список в качестве ответа. Также тут можно использовать для проверки метод whenRoute.
    	$httpBackend.whenGET(/^\/users\?.+$/i).respond(200, [{id: 1, name: 'Albert', lastName: 'Einstein'}]);
    	// А все остальные запросы (тут надо перечислить все ваши возможные методы HTTP-запросов) мы будем спрашивать у настоящего бэкенда
    	angular.forEach(['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], function (method) {
    		$httpBackend.when(method, /.*/i).passThrough();
    	});
    }]);
  3. Подключаем файл с нашим AngularJS-модулем к странице;
  4. Добавляем модуль к зависимостям вашего основного модуля;
  5. Разрабатываем новую функциональность на мнимо-полноценном настроенном API;
  6. По окончании разработки не забываем отключить angular-mocks и наш модуль с заглушками, убрать зависимость в основном модуле.

Особо хотелось бы обратить ваше внимание на последний пункт. Если этого не сделать, думаю, вы представляете, как будет интересно пользователям работать с обновлённым приложением без реальных действий сервера по некоторым запросам :)

Ответы в заглушках можно сделать и динамическими. Для этого в метод respond() можно передать функцию. Подробнее об этом можно почитать в документации фрэймворка.

Подведение итогов

Теперь, в отличие от метода со статическими файлами на сервере, мы можем гораздо гибче настраивать заглушки запросов нашего сервера (вплоть до каждого адреса, заголовка и параметра), при этом даже не покидая пределы проекта в вашей любимой IDE.

Вот и всё! Я надеюсь, что приём, который я вам только что показал, будет вам полезен в разработке новых фич, которые в скором времени будут приятно удивлять всё новых и новых пользователей ваших приложений!

До новых статей! :)