Узнайте, как имитировать серверную часть с помощью HttpIntercetor, чтобы упростить и упростить тестирование.

Как следует из названия, HttpInterceptor используется для перехвата HTTP-вызовов (запросов и ответов) из нашего приложения Angular. С HttpInterceptors мы можем сделать много вещей:

  • Аутентификация
  • Кеширование
  • Поддельный бэкэнд
  • Преобразование URL
  • Изменение заголовков
  • и так далее…

В этом посте мы увидим, как имитировать бэкэнд с помощью HttpInterceptors.

Подделка серверной части

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

HttpInterceptor был представлен в Angular 4.0.3. Хотя это очень блестящее дополнение, у него есть некоторые особенности, которые мы должны принять к сведению из Angular Docs:

  • Хотя перехватчики могут изменять запросы и ответы, свойства экземпляра HttpRequest и HttpResponse доступны только для чтения, что делает их в значительной степени неизменными.

Чтобы изменить HttpRequest, мы используем метод clone.

  • Angular применяет перехватчики в том порядке, в котором вы их предоставляете. Если вы предоставите перехватчики A, затем B, затем C, запросы будут поступать в A - ›B -› C, а ответы будут исходить C - ›B -› A. Вы не можете изменить порядок или удалить перехватчики позже. Если вам нужно динамически включать и отключать перехватчик, вам придется встроить эту возможность в сам перехватчик.

Сначала мы создаем класс BackendInterceptor:

@Injectable()
class BackendInterceptor implements HttpInterceptor {
   ...
}

Видите ли, все начинается с реализации интерфейса HttpInterceptor. Это Angular, подчиняющийся DIP (принципу инверсии зависимостей). Метод запроса в HttpClient вызывается всякий раз, когда мы выполняем вызов HTTP (GET, POST, DELETE, OPTIONS), вызов проходит через HttpHandler, этому классу передается параметр, который должен иметь экземпляр HttpInterceptor, потому что HttpHandler вызовет метод intercept на Это. Итак, Angular гарантирует, что мы реализуем интерфейс HttpInterceptor, поэтому мы передаем любой тип HttpInterceptor, который мы хотим, без нарушения модуля Http. Прохладный!!!

Итак, мы добавим метод перехвата в наш класс BackendHttpInterceptor:

@Injectable()
class BackendInterceptor implements HttpInterceptor {
 constructor(private injector: Injector) {}
 intercept(request: HttpRequest, next: HttpHandler): Observable<HttpEvent<any>> {
   ...
 }
}

Мы вставим нашу реализацию между скобками-перехватчиками. Что бы мы там ни делали, мы должны передать HttpRequest в цепочку, вернув Observable с переданным в него Http-запросом.

Здесь мы перехватим вызов Http и вернем Observable HttpResponse. Это предотвратит попадание HTTP-запроса в последнюю цепочку к методу XmlHttpRequest#send.

Совет: Еще один отличный инструмент для разработчиков Angular - Bit (Github). Bit позволяет легко делиться и совместно работать над компонентами Angular. Сотрудничайте над общими компонентами, чтобы поддерживать единообразный и пуленепробиваемый пользовательский интерфейс и быстрее работать в команде.



Создание поддельных данных сервера

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

...
const usersData = {
 "users": [
 {
   "name": "chidume nnamdi",
   "age": 26
 },
 {
   "name": "chisom",
   "age": 46
 },
 {
   "name": "elvis",
   "age": 21
 },
 {
   "name": "osy mattew",
   "age": 21
 },
 {
   "name": "valentine",
   "age": 21
 },
 ]
}
@Injectable()
class BackendInterceptor implements HttpInterceptor {
 constructor(private injector: Injector) {}
 intercept(request: HttpRequest, next: HttpHandler): Observable<HttpEvent<any>> {
  ...
 }
}

Свойство user в usersData содержит список имени и возраста нашего пользователя, который нам нужен для нашего приложения Angular.

Мы можем поместить его в отдельный файл JSON или внутри класса, как мы только что сделали.

Теперь давайте вернем usersData в наш класс BackendHttpInterceptor.

Возврат данных

...
const usersData = {
 "users": [
    {
      "name": "chidume nnamdi",
      "age": 26
    },
    {
       "name": "chisom",
       "age": 46
    },
    {
        "name": "elvis",
        "age": 21
    },
    {
        "name": "osy mattew",
        "age": 21
    },
    {
        "name": "valentine",
        "age": 21
    },
 ]
}
@Injectable()
class BackendInterceptor implements HttpInterceptor {
 constructor(private injector: Injector) {}
 intercept(request: HttpRequest, next: HttpHandler): Observable<HttpEvent<any>> {
      return of(new HttpResponse({ status: 200, body: usersData }));
 }
}

Мы создали HttpResponse со статусом 200 (что означает ОК для браузера) и body полезной нагрузкой, установленной для наших usersData. Мы передали экземпляр в вызов Observable#of(...) и вернули Observable.

Мы просто вернули usersData. При этом все запросы Http будут возвращать usersData. Мы бы изменили, чтобы соответствовать URL-адресу, когда API запрашивается с помощью метода GET. Вроде от localhost:4200/users/.

Итак, наш BackendHttpInceptor будет таким:

...
@Injectable()
class BackendInterceptor implements HttpInterceptor {
 constructor(private injector: Injector) {}
 intercept(request: HttpRequest, next: HttpHandler): Observable<HttpEvent<any>> {
     if(request.method === "GET" && request.url === "http://localhost:4200/users") {
          return of(new HttpResponse({ status: 200, body: usersData }));
     }
     next.handle(request)
 }
}

Что мы сделали? Мы использовали параметр request для экземпляра HttpRequest, этот HttpRequest представляет исходящий HTTP-запрос, включая URL-адрес, метод, заголовки, тело и другие параметры конфигурации запроса. Мы проверяем, является ли метод запроса GET методом, а URL-адрес равен http://localhost:4200/users, API, который мы используем для получения usersData данных. При равенстве мы знаем, что запрашиваем данные пользователя, поэтому мы обслуживаем usersData в качестве ответа и разрезаем цепочку, в противном случае мы передаем запрос на следующую строку в цепочке, используя вызов next.request(), параметр next является экземпляром HttpHandler this call отправит запрос другому перехватчику в цепочке до тех пор, пока он в конечном итоге не достигнет HttpBackend, последнего HttpHandler, который отправляет запрос к HTTP API браузера на серверную часть.

Таким образом, наш BackendHttpInterceptor будет давать нам поддельные данные, когда мы вызываем http: // localhost: 4200 / users в нашем компоненте или сервисе.

@Component({
 ...
})
export class AppComponent {
 users: Array<User>
 constructor(private httpClient: HttpClient) {}
 ngOnInit() {
   this.httpClient.get("http://localhost:4200:/users").subscribe( res => this.users = res.users)
 }
}
...
@Injectable()
export class UserService {
 getUsers(): Observable {
   return this.httpClient.get("http://localhost:4200:/users")
 }
}

не доходя до фактического серверного бэкэнда.

Регистрируясь на нашем BackendHttpInterceptor

Мы добавим BackendHttpInterceptor в наш основной модуль, здесь, в нашем проекте, это будет AppModule:

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

Видите ли, чтобы зарегистрировать наш перехватчик, мы создаем объект со свойством provide, установленным на токен HTTP_INTERCEPTORS, экспортируемым CommonModule, и свойством useClass, установленным на наш BackendHttpInterceptor. Объект добавляется в массив providers в декораторе NgModule.

Мы сделали. Так просто, мы просто обслуживаем наше приложение ng serve и тестируем его.

Заключение

Отличные перехватчики !! Нам осталось придумать что-нибудь отличное, что можно было бы с ним реализовать.

В следующих статьях мы рассмотрим варианты использования HttpInterceptor, которые мы перечислили в начале. Будьте на связи.

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

Спасибо !!!

Учить больше