HTTP — Перехват запросов и ответов¶
16.03.2023
С помощью перехвата вы объявляете перехватчики, которые проверяют и преобразуют HTTP-запросы от вашего приложения к серверу. Эти же перехватчики могут проверять и преобразовывать ответы сервера на обратном пути к приложению.
Несколько перехватчиков образуют цепочку обработчиков запросов/ответов вперед-назад.
Перехватчики могут выполнять различные неявные задачи, от аутентификации до протоколирования, обычным, стандартным способом для каждого запроса/ответа HTTP.
Без перехвата разработчикам пришлось бы реализовывать эти задачи явно для каждого вызова метода HttpClient
.
Написать перехватчик¶
Для реализации перехватчика объявите класс, который реализует метод intercept()
интерфейса HttpInterceptor
.
Вот перехватчик noop
, который пропускает запрос через себя, не трогая его:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Метод intercept
преобразует запрос в Observable
, который в итоге возвращает HTTP-ответ. В этом смысле каждый перехватчик способен обрабатывать запрос полностью самостоятельно.
Большинство перехватчиков проверяют запрос на входе и передают потенциально измененный запрос в метод handle()
объекта next
, который реализует интерфейс HttpHandler
.
1 2 3 |
|
Как и intercept()
, метод handle()
преобразует HTTP-запрос в Observable
из HttpEvents
, которые в конечном итоге включают ответ сервера. Метод intercept()
может проверить эту наблюдаемую и изменить ее, прежде чем вернуть ее вызывающей стороне.
Этот no-op
перехватчик вызывает next.handle()
с исходным запросом и возвращает наблюдаемую, ничего не делая.
Объект next
¶
Объект next
представляет следующий перехватчик в цепочке перехватчиков. Последний next
в цепочке — это обработчик бэкенда HttpClient
, который посылает запрос на сервер и получает ответ сервера.
Большинство перехватчиков вызывают next.handle()
, так что запрос проходит через следующий перехватчик и, в конечном счете, через внутренний обработчик. Перехватчик может пропустить вызов next.handle()
, замкнуть цепочку и вернуть свою собственную Observable
с искусственным ответом сервера.
Это распространенный паттерн промежуточного ПО, встречающийся в таких фреймворках, как Express.js.
Предоставьте перехватчик¶
Перехватчик NoopInterceptor
— это сервис, управляемый системой dependency injection (DI) Angular. Как и другие сервисы, вы должны предоставить класс перехватчика, прежде чем приложение сможет его использовать.
Поскольку перехватчики являются необязательными зависимостями сервиса HttpClient
, вы должны предоставить их в том же инжекторе или в родительском инжекторе, который предоставляет HttpClient
. Перехватчики, предоставленные после того, как DI создаст HttpClient
, игнорируются.
Это приложение предоставляет HttpClient
в корневом инжекторе приложения, как побочный эффект импорта HttpClientModule
в AppModule
. Вы также должны предоставить перехватчики в AppModule
.
После импорта инжекторного маркера HTTP_INTERCEPTORS
из @angular/common/http
, напишите провайдер NoopInterceptor
следующим образом:
1 |
|
Обратите внимание на опцию multi: true
. Этот обязательный параметр сообщает Angular, что HTTP_INTERCEPTORS
является маркером для мультипровайдера, который инжектирует массив значений, а не одно значение.
Вы можете добавить этот провайдер непосредственно в массив провайдеров AppModule
. Однако это довольно многословно, и велика вероятность того, что вы создадите больше перехватчиков и предоставите их таким же образом.
Вы также должны обратить пристальное внимание на порядок, в котором вы предоставляете эти перехватчики.
Подумайте о создании "бочкового" файла, который собирает все провайдеры перехватчиков в массив httpInterceptorProviders
, начиная с этого первого, NoopInterceptor
.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Затем импортируйте и добавьте его в AppModule
providers array
следующим образом:
1 2 3 |
|
По мере создания новых перехватчиков, добавляйте их в массив httpInterceptorProviders
, и вам не придется снова обращаться к AppModule
.
В полном коде примера перехватчиков гораздо больше.
Порядок перехватчиков¶
Angular применяет перехватчики в том порядке, в котором вы их предоставляете. Например, рассмотрим ситуацию, в которой вы хотите обрабатывать аутентификацию ваших HTTP-запросов и записывать их в журнал перед отправкой на сервер.
Для выполнения этой задачи вы можете предоставить службу AuthInterceptor
, а затем службу LoggingInterceptor
.
Исходящие запросы будут поступать от AuthInterceptor
к LoggingInterceptor
.
Ответы на эти запросы будут поступать в обратном направлении, от LoggingInterceptor
обратно к AuthInterceptor
.
Ниже представлено визуальное представление этого процесса:
Последним перехватчиком в процессе всегда является HttpBackend
, который обрабатывает связь с сервером.
Вы не сможете изменить порядок или удалить перехватчики позже. Если вам нужно включать и отключать перехватчик динамически, вам придется встроить эту возможность в сам перехватчик.
Обработка событий перехватчика¶
Большинство методов HttpClient
возвращают наблюдаемые HttpResponse<any>
. Сам класс HttpResponse
фактически является событием, тип которого HttpEventType.Response
.
Однако один HTTP-запрос может генерировать несколько событий других типов, включая события о ходе загрузки и выгрузки.
Методы HttpInterceptor.intercept()
и HttpHandler.handle()
возвращают наблюдаемые HttpEvent<any>
.
Многие перехватчики занимаются только исходящим запросом и возвращают поток событий из next.handle()
, не изменяя его. Некоторые перехватчики, однако, должны изучить и модифицировать ответ от next.handle()
; эти операции могут видеть все эти события в потоке.
Хотя перехватчики способны изменять запросы и ответы, свойства экземпляров HttpRequest
и HttpResponse
являются readonly
, что делает их в значительной степени неизменяемыми. Они неизменяемы по веской причине:
Приложение может повторять запрос несколько раз, прежде чем он увенчается успехом, что означает, что цепочка перехватчиков может обрабатывать один и тот же запрос несколько раз.
Если перехватчик может изменить исходный объект запроса, то повторная операция будет начинаться с измененного запроса, а не с исходного.
Неизменность гарантирует, что перехватчики видят один и тот же запрос при каждой попытке.
Ваш перехватчик должен возвращать каждое событие без изменений, если только у него нет веских причин поступать иначе.
TypeScript не позволяет устанавливать свойства HttpRequest
только для чтения.
1 2 |
|
Если вам необходимо изменить запрос, сначала клонируйте его и измените клон перед передачей его в next.handle()
. Вы можете клонировать и изменить запрос за один шаг, как показано в следующем примере.
1 2 3 4 5 6 |
|
Хэш-аргумент метода clone()
позволяет вам изменять определенные свойства запроса, копируя остальные.
Изменение тела запроса¶
Защита присваивания readonly
не может предотвратить глубокие обновления и, в частности, не может помешать вам изменить свойство объекта тела запроса.
1 |
|
Если вам необходимо изменить тело запроса, выполните следующие действия.
- Скопируйте тело запроса и внесите изменения в копию.
- Клонируйте объект запроса, используя его метод
clone()
. - Замените тело клона измененной копией.
1 2 3 4 5 6 |
|
Очистка тела запроса в клоне¶
Иногда требуется очистить тело запроса, а не заменить его. Для этого установите для клонированного тела запроса значение null
.
Если вы установите для клонированного тела запроса значение undefined
, Angular предположит, что вы намерены оставить тело как есть.
1 2 3 |
|