Как сделать угловой модуль, чтобы игнорировать перехватчик http, добавленный в основной модуль
У меня есть основной модуль с HttpInterceptor для обработки авторизации, и я включаю этот модуль в AppModule, таким образом, все другие модули, использующие HttpClient, используют этот перехватчик.
@NgModule({
imports: [],
declarations: [],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true,
},
]
})
export class CoreModule { }
Как заставить модуль обходить перехватчик по умолчанию?
@NgModule({
imports: [
CommonModule
],
declarations: components,
providers: [CustomService],
exports: components,
})
export class ModuleWithoutInterceptorModule { }
5 ответов
В соответствии с этим предложением на GitHub мы внедрили простой заголовок для определения запросов, которые не должны быть перехвачены. В перехватчике:
export const InterceptorSkipHeader = 'X-Skip-Interceptor';
@Injectable()
export class SkippableInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.headers.has(InterceptorSkipHeader)) {
const headers = req.headers.delete(InterceptorSkipHeader);
return next.handle(req.clone({ headers }));
}
... // intercept
}
}
Затем всякий раз, когда вы хотите пропустить перехват для определенного запроса:
const headers = new HttpHeaders().set(InterceptorSkipHeader, '');
this.httpClient
.get<ResponseType>(someUrl, { headers })
...
Обратите внимание, что с помощью этого метода служба, а не перехватчик, выбирает, когда применяется логика перехватчика; это означает, что службы должны "что-то" знать о перехватчиках в вашем приложении. В зависимости от вашего варианта использования, может быть лучше, чтобы перехватчики решали, когда применять логику.
Вы можете использовать HttpBackend.
Пример:
import { HttpClient, ..., HttpBackend } from '@angular/common/http';
@Injectable()
export class TestService {
private httpClient: HttpClient;
constructor( handler: HttpBackend) {
this.httpClient = new HttpClient(handler);
}
....
Таким образом, служба не перехватывается AuthInterceptor.
Чтобы обойти все перехватчики, мы можем использовать HttpBackend, как предлагает @deg.
В других случаях мы можем создать фабрику HttpClient, которая позволит нам исключить перехватчики из цепочки перехватчиков:
import { createHttpClient } from './http-client.factory';
...
@Injectable({
providedIn: 'root'
})
export class TodosApiService {
http = createHttpClient(this.injector, [Interceptor2]);
^^^^^^^^^^^^
Interceptors to exclude
constructor(private injector: Injector) { }
getTodo() {
// Interceptor2 will be bypassed
return this.http.get('https://jsonplaceholder.typicode.com/todos')
}
}
Обратите внимание, что вы можете повторно использовать эту логику, создав базовый класс:
@Injectable()
export class BasicHttpClient {
protected http = createHttpClient(this.injector, [Interceptor2]);
constructor(private injector: Injector) { }
}
@Injectable({ providedIn: 'root' })
export class TodosApiService extends BaseHttpClient {
getTodo() {
// Interceptor2 will be bypassed
return this.http.get('https://jsonplaceholder.typicode.com/todos')
}
}
http-client.factory.ts
import {
HTTP_INTERCEPTORS,
HttpBackend,
HttpClient,
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { Injector, Type } from '@angular/core';
class HttpInterceptorHandler implements HttpHandler {
constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {}
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
return this.interceptor.intercept(req, this.next);
}
}
class HttpInterceptingHandler implements HttpHandler {
private chain: HttpHandler | null = null;
constructor(
private backend: HttpBackend,
private injector: Injector,
private interceptorsToExclude: Type<HttpInterceptor>[],
private intercept?: (req: HttpRequest<any>) => HttpRequest<any>
) {}
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
if (this.intercept) {
req = this.intercept(req);
}
if (this.chain === null) {
const interceptors = this.injector
.get(HTTP_INTERCEPTORS, [])
.filter(
interceptor => !this.interceptorsToExclude.some(interceptorType => interceptor instanceof interceptorType)
);
this.chain = interceptors.reduceRight(
(next, interceptor) => new HttpInterceptorHandler(next, interceptor),
this.backend
);
}
return this.chain.handle(req);
}
}
export function createHttpClient(
injector: Injector,
excludedInterceptors: Type<HttpInterceptor>[],
intercept?: (req: HttpRequest<any>) => HttpRequest<any>
) {
return new HttpClient(
new HttpInterceptingHandler(injector.get(HttpBackend), injector, excludedInterceptors, intercept)
);
}
Начиная с Angular 12, это можно сделать с помощьюHttpContext
недвижимость дляHttpRequest
!
Этот подход удобен тем, что не требует никакой логики для удаления заголовков и может выполняться для каждого запроса отдельно.
export const DISABLE_GLOBAL_EXCEPTION_HANDLING = new HttpContextToken<boolean>(() => false);
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.ignoreErrorHandling(request)) {
return next.handle(request);
}
return next.handle(request).pipe(
catchError((error) => {
// normal intercepting logic
return throwError(() => error);
}),
);
private ignoreErrorHandling(request: HttpRequest<any>) {
return request.context.get(DISABLE_GLOBAL_EXCEPTION_HANDLING);
}
Отдельный запрос можно отключить, добавив токен:
this.httpClient.get<ResponseType>(`/api`, {
context: new HttpContext().set(DISABLE_GLOBAL_EXCEPTION_HANDLING, true),
});
Если используется в зависимости вне вашего контроля (например, в сторонней библиотеке), вы все равно можете использоватьHttpContextTokens
которые были добавлены в Angular v12 с помощьюHttpClient
заводской поставщик. Вы можете распространить это на отдельные модули или компоненты, используя массив:
отключить-interceptor.handler.ts
import { HttpContextToken, HttpHandler, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
export const DISABLE_INTERCEPTORS = new HttpContextToken<boolean>(() => false);
@Injectable()
export class DisableInterceptorHandler extends HttpHandler {
constructor(private httpHandler: HttpHandler) {
super();
}
handle(req: HttpRequest<unknown>) {
return this.httpHandler.handle(
req.clone({
context: req.context.set(DISABLE_INTERCEPTORS, true),
}),
);
}
}
Предоставьте обработчик в вашем компоненте/модуле/сервисе/независимоproviders
множество
providers: [
DisableInterceptorHandler,
{
provide: HttpClient,
useFactory: (handler: DisableInterceptorHandler) => new HttpClient(handler),
deps: [DisableInterceptorHandler],
},
],
Уважайте HttpContextToken в своем перехватчике!
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { DISABLE_INTERCEPTORS } from './disable-interceptor.handler';
@Injectable()
export class AuthenticationInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
if (request.context.get(DISABLE_INTERCEPTORS) === true) {
return next.handle(request);
}
return next.handle(request).pipe(
// add your typical interceptor logic
);
}
}