HTTP-перехватчики предоставляют гибкий механизм управления вашим приложением при работе с сетевыми ресурсами. Они похожи на промежуточное программное обеспечение в других фреймворках и позволяют абстрагировать и повторно использовать сетевую логику.
Ресурсы
🚀 Демо-приложение / 🛠 Github
Зачем использовать HTTP-перехватчики
Перехватчики обеспечивают удобное расположение для применения функций ко всем или некоторым сетевым запросам и ответам. По мере роста приложения повторная реализация той же логики может стать утомительной, подверженной ошибкам и потенциально может привести к несовместимой функциональности.
Например, установка заголовка авторизации для нескольких сетевых запросов может быстро привести к дублированию кода на уровне службы или компонента. Используя перехватчик, его можно настроить один раз и применить ко всем существующим и будущим HTTP-запросам. Абстрагируя глобальную сетевую логику до единого класса ответственности, мы упрощаем тестирование и быстрое создание надежных приложений.
Перехват запросов
Запросы можно контролировать с помощью HttpHandler
, который передается методам перехватчика. В простейшем случае, если вы не хотите изменять запрос, вы возвращаете handle
метод. Обычно используется так:
intercept(req: HttpRequest<any>, next: HttpHandler) { return next.handle(req) }
Здесь вы можете изменить Заголовки аутентификации или что-нибудь еще, связанное с запросом.
intercept(req: HttpRequest<any>, next: HttpHandler) { // Get the auth token from the service. const authToken = this.auth.getAuthorizationToken(); // Clone the request and replace the original headers with // cloned headers, updated with the authorization. const authReq = req.clone({ headers: req.headers.set('Authorization', authToken) }); // send cloned request with header to the next handler. return next.handle(authReq); }
Перехват ответов
Чтобы взаимодействовать с ответом, вы pipe
отключите метод handle
. Отсюда вы можете взаимодействовать с другими службами, например добавлять в кеш, как показано ниже, или изменять ответ в примере XML на JSON.
Имейте в виду, что помимо ответа могут быть и другие события, поэтому рекомендуется проверить HttpResponse
, прежде чем действовать в ответ на событие ответа.
return next.handle(req).pipe( // There may be other events besides the response. filter(event => event instanceof HttpResponse), tap((event: HttpResponse<any>) => { cache.set(req.urlWithParams, { key: req.urlWithParams, body: event.body, dateAdded: Date.now(), }); }) );
Примеры
Кеширование
Кэширование, в частности инвалидация кеша, может быть довольно сложной задачей, поэтому в этой статье мы не будем подробно останавливаться на этом. Вместо этого мы рассмотрим простой пример, который возвращает что-то из кеша или позволяет выполнить запрос. Для данной конечной точки вы можете кэшировать результаты несколькими способами и возвращать кеш в зависимости от времени, существования или другого фактора.
В следующем примере проверяется наличие в кеше и возвращается:
export class CachingInterceptor implements HttpInterceptor { constructor(private cache: CacheService) {} intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { // continue request if not cacheable. if (!this.canCache(req)) { return next.handle(req); } const cachedResponse = this.cache.get(req.urlWithParams); // return HttpResponse so that HttpClient methods return a value return cachedResponse ? of(new HttpResponse({ body: cachedResponse.body })) : sendRequest(req, next, this.cache); } canCache(req: HttpRequest<any>): boolean { // only cache `todo` routes return req.url.includes('todos'); } }
Если запроса нет в кеше, выполните обычный запрос с sendRequest
.
function sendRequest( req: HttpRequest<any>, next: HttpHandler, cache: CacheService ): Observable<HttpEvent<any>> { return next.handle(req).pipe( // There may be other events besides the response. filter(event => event instanceof HttpResponse), tap((event: HttpResponse<any>) => { cache.set(req.urlWithParams, { key: req.urlWithParams, body: event.body, dateAdded: Date.now(), }); }) ); }
Здесь мы pipe
выключаем handle
метод взаимодействия с ответом и помещаем его в кэш. Теперь, если будет сделан такой же запрос, он будет немедленно возвращен.
const cachedResponse = this.cache.get(req.urlWithParams); return cachedResponse ? of(new HttpResponse({ body: cachedResponse.body })) : sendRequest(req, next, this.cache);
XML в JSON
Часто разработчики не могут полностью контролировать способ получения данных. Например, если у вас есть один API, который возвращает XML, но остальная часть вашего приложения работает с JSON, может иметь смысл преобразовать ответ XML в JSON для согласованности. С помощью перехватчика условную логику XML можно абстрагировать от потребителей и применять ко всем сетевым запросам.
export class XmlInterceptor implements HttpInterceptor { constructor(@Inject(XmlParser) private xml: XMLParser) {} intercept(req: HttpRequest<any>, next: HttpHandler) { // extend server response observable with logging return next.handle(req).pipe( // proceed when there is a response; ignore other events filter(event => event instanceof HttpResponse), map( (event: HttpResponse<any>) => { if (this.xml.validate(event.body) !== true) { // only parse xml response, pass all other responses to other interceptors return event; } // {responseType: text} expects a string response return event.clone({ body: JSON.stringify(this.xml.parse(event.body)) }); }, // Operation failed; error is an HttpErrorResponse error => event ) ); } }
Чтобы обработать ответ, эта логика pipe
отключена от handle
метода. Затем, если ответ является допустимым XML, мы возвращаем клонированный ответ event
и анализируем XML в JSON.
Перенаправление в зависимости от объема
Когда приложению необходимо ограничить доступ к определенным маршрутам, перехватчик может предоставить эту функциональность в одном месте по множеству маршрутов. Поскольку перехватчики запускаются для каждого запроса, вы можете организовать свою логику защиты маршрута в одном месте. Предполагая, что вы настроили охраняемые маршруты и можете получить доступ к доступным областям действия пользователя, перехватчик может определить, разрешить ли запрос или перенаправить.
export class ScopesInterceptor implements HttpInterceptor { constructor(private scopesService: ScopesService, private router: Router) {} intercept(req: HttpRequest<any>, next: HttpHandler) { // not protected, pass request through if (!this.scopesService.protectedRoutes(req.urlWithParams)) { return next.handle(req); } // route is protected, only allow admins if (this.scopesService.isAdmin) { return next.handle(req); } else { // not admin, redirect and cancel request this.router.navigate(['404']); return of(undefined); } } }
В этом примере, если маршрут не защищен, мы не предпринимаем никаких действий. Если это так, запрос разрешен, если пользователь является администратором, в противном случае мы перенаправляем и отменяем запрос.
Неизменность
Это может быть неочевидно, но экземпляры HttpRequest
и HttpResponse
неизменяемы. Из Angular docs
перехватчики способны изменять запросы и ответы, свойства экземпляра HttpRequest и HttpResponse доступны только для чтения, что делает их в значительной степени неизменными.
Это сделано потому, что приложение может повторить запрос несколько раз, и если перехватчик может изменить исходный запрос, каждый последующий запрос может быть другим.
Для разработчиков это означает, что вы должны clone
запрос и изменить этот экземпляр:
// clone request and replace 'https://' with 'https://' at the same time const secureReq = req.clone({ url: req.url.replace('https://', 'https://') }); // send the cloned, "secure" request to the next handler. return next.handle(secureReq);
Приказ перехватчика
Перехватчики передают запросы в том порядке, в котором они предоставлены [A,B,C]
. Следующий пример ствола собирает перехватчики, которые позже будут предоставлены в объявленном порядке.
запросы будут течь в A- ›B-› C, а ответы будут течь в C- ›B-› A.
export const httpInterceptorProviders = [ { provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true }, ];
затем в app.module.ts:
providers: [ httpInterceptorProviders ],
Резюме
Как мы видели в этих примерах, перехватчики обеспечивают простой механизм взаимодействия с HTTP-запросами и ответами. Это упрощает добавление уровней управления и обеспечивает большую функциональность во всем приложении без дублирования логики.
Полезные ссылки
Первоначально опубликовано на сайте kevinschuchard.com 8 мая 2019 г.