Изучите 4 различных способа имитации ответов бэкенда в Angular и выберите решение, которое лучше всего соответствует вашим потребностям.

В этой статье мы покажем, как имитировать ответы бэкенда в Angular. Мы изучим 4 разных способа имитировать ответы сервера, используя:

  1. функция of (RxJs),
  2. an HttpInterceptor,
  3. Веб-API в памяти или
  4. пакет json-сервер.

В процессе мы представим минусы и плюсы каждого решения и сведем их в таблицу. Наконец, мы выделим, когда лучше всего использовать каждое решение.

Давайте начнем!

Функция of (RxJs)

Это самый простой и быстрый способ имитировать ответы бэкенда. Мы временно заменяем запрос на сами данные.

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

@Injectable({
  providedIn: 'root'
})
export class PandaService {

  public getPandas(): Observable<Panda[]> {
    return of(PANDAS);
  }
}

Мы делаем это, чтобы сохранить тип возвращаемого значения неизменным и свести к минимуму общие необходимые изменения. Как только мы подпишемся на возвращаемый наблюдаемый объект, мы получим фиктивные данные.

К сожалению, «самый быстрый» не всегда означает «лучший». Что, если мы хотим имитировать несколько запросов? Это очень быстро надоедает.

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

Там должен быть лучший путь!

HttpInterceptor

В одной из наших предыдущих статей мы подробно изучали перехватчики. Один из вариантов использования, который мы рассмотрели, — насмешка над HTTP-ответами.

В этом решении мы оставляем запрос и сервис нетронутыми.

@Injectable({
  providedIn: 'root'
})
export class PandaService {
  constructor(private http: HttpClient) {}

  public getPandas(): Observable<Panda[]> {
    return this.http.get<Panda[]>('api/pandas');
  }
}

Вместо этого мы создаем HttpInterceptor для перехвата потока запросов и ответов. В нашем случае мы хотим заменить ответ на запрос.

@Injectable()
export class MockBackendInterceptor implements HttpInterceptor {

  private readonly MOCK_RESPONSE = of(
    new HttpResponse({
      status: 200,
      statusText: 'OK',
      body: PANDAS
    })
  ).pipe(delay(300));

  intercept(request: HttpRequest<any>, next: HttpHandler) {
    if (request.method === 'GET' && request.url.endsWith('/pandas')) {
      return this.MOCK_RESPONSE;
    } else {
      return next.handle(req);
    }
  }

}

Наконец, мы регистрируем перехватчик в массиве providers файла AppModule.

@NgModule({
  // ...
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MockBackendInterceptor,
      multi: true
    }
  ]
})
export class AppModule {}

Изменения теперь сгруппированы в файл. Мы также можем отменить регистрацию перехватчика в одной точке, закомментировав соответствующую строку в массиве providers.

Но теперь может возникнуть другая проблема. Представьте, что вам нужно имитировать несколько HTTP-запросов от нескольких служб. Тогда нам понадобится либо несколько перехватчиков, либо очень длинный список блоков if-else-if.

Можем ли мы сделать лучше?

Веб-API в памяти

Еще один способ имитировать ответы бэкенда в Angular — использовать In-memory Web API. Это решение требует установки модуля.

Мы устанавливаем пакет In-memory Web API с помощью следующей команды:

npm install --save-dev angular-in-memory-web-api

Обратите внимание, что это не должно использоваться в производстве. Следовательно, параметр --save-dev.

Затем мы импортируем HttpClientInMemoryWebApiModule в наш файл AppModule.

@NgModule({
  imports: [
    HttpClientModule,
    // Intercepts HTTP requests and returns simulated server responses.
    // Remove it when a real server is ready to receive requests.
    HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, {
      dataEncapsulation: false
    })
  ],
  // ...
})
export class AppModule {}

Нам не нужно вносить какие-либо изменения в исходный файл PandaService.

@Injectable({
  providedIn: 'root'
})
export class PandaService {
  constructor(private http: HttpClient) {}

  public getPandas(): Observable<Panda[]> {
    return this.http.get<Panda[]>('api/pandas');
  }
}

URL-адрес запроса должен иметь формат: base/collectionName. Итак, в предыдущем фрагменте мы объявляем, что имя коллекции будет pandas.

Затем мы создаем InMemoryDataService и переопределяем метод createDb.

@Injectable({
  providedIn: 'root'
})
export class InMemoryDataService implements InMemoryDbService {
  createDb() {
    return { pandas: PANDAS };
  }
}

Имя свойства, которому мы назначаем фиктивные данные, должно соответствовать имени коллекции с учетом регистра,отсюда и { pandas: PANDAS }. Возврат только { PANDAS } вызовет ошибку!

Вот и все! HttpClientInMemoryWebApiModule перехватит запрос api/pandas и вернет фиктивный ответ.

Все изменения в одном файле. И мы можем деактивировать решение из одной точки. Мы просто удаляем или комментируем импорт в массиве imports AppModule, когда реальный сервер готов принимать запросы.

Что ж, нам все еще нужно создать этот дополнительный сервис и написать код.

json-сервер

Последнее решение — использовать пакет json-server для создания мок-бэкенда. Это решение не требует никаких изменений в нашем коде.

Мы также можем выбрать установить и запустить локально или ничего не устанавливать и запустить онлайн. Имейте в виду, что онлайн-опция имеет ограничения *.

Оба варианта требуют создания файла db.json, который настраивает, как фиктивный бэкэнд будет реагировать на каждый вызов конечной точки.

Мы покажем, как запустить это локально.

Во-первых, мы устанавливаем пакет json-server с помощью следующей команды:

npm install -g json-server

Затем мы создаем файл db.json в корне нашего приложения:

{
  "pandas": [
    {
      "id": 1,
      "name": "Bao Bao",
      "sex": "F",
      "birthDate": "23/08/2013",
      "currentLocation": "Wolong National Nature Reserve"
    },
    ...
  ]
}

Если мы запустим json-server --watch db.json, мы получим следующие сообщения:

Есть две проблемы с этим. Исходный запрос — localhost:4200/api/pandas, а наша фиктивная конечная точка — localhost:3000/pandas.

Первая проблема — это порт. Вторая проблема — несоответствие из-за части /api.

Первую проблему решаем с помощью прокси. Мы создаем файл proxy.conf.json в корне нашего приложения:

{
  "/api": {
    "target": "http://localhost:3000"
  }
}

В файле angular.json мы добавляем пару ключ-значение "proxyConfig": "proxy.conf.json" к options цели serve:

...,
"serve": {
  ...,
  "options": {
    "proxyConfig": "proxy.conf.json"
  }
},
...

Чтобы решить вторую проблему, нам нужно сопоставить /api/pandas с /pandas. Для этого мы создаем файл routed.json в корне нашего приложения:

{
  "/api/*": "/$1"
}

Теперь мы можем, наконец, запустить наш сервер:

json-server --watch db.json --routes routes.json

Вот и все! Запросы будут сопоставлены и проксированы на наш фиктивный бэкенд, и, таким образом, будут получены фиктивные ответы.

Ниже мы приводим сводную таблицу с минусами и плюсами каждого решения. Выберите решение, которое наилучшим образом соответствует вашим потребностям, в зависимости от вашего случая.

Вы можете найти полный исходный код в этом репозитории GitHub.

Обратите внимание, что мы включаем все решения в этот репозиторий. Итак, вам нужно будет закомментировать несколько строк, чтобы одно решение оставалось активным.

Выводы

Есть несколько способов имитировать ответы бэкенда в Angular. Мы представили четыре из них. Независимо от решения, нам необходимо предоставить данные. Разница в деталях.

Используйте функцию of (RxJs), если вам нужно имитировать простой запрос или два.

Используйте HttpInterceptor, если вам нужно имитировать больше запросов, но не так много.

Используйте In-memory Web API, если вам нужно имитировать несколько запросов, но при этом вы хотите свести изменения кода к минимуму.

Используйте пакет json-server, если вы вообще не хотите изменять свой код, но можете создавать некоторые файлы конфигурации.

Надеюсь, вам понравилась эта статья и вы узнали что-то новое. Если вы это сделали, подпишитесь на мою рассылку, чтобы получать больше подобного контента.

Спасибо, что прочитали. Следите за новостями.

Планируете присоединиться к Medium? Членство стоит 5 долларов в месяц и дает неограниченный доступ ко всем историям на Medium. Используйте мою реферальную ссылку:



Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .

Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.