【问题标题】:How to make an angular module to ignore http interceptor added in a core module如何使角度模块忽略核心模块中添加的http拦截器
【发布时间】:2023-03-07 06:12:01
【问题描述】:

我确实有一个带有用于授权处理的 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 { }

【问题讨论】:

  • 我也想不通。我认为正确的方法是在应用程序和功能模块之间创建中间层“拦截”和“未拦截”模块,并在“未拦截”模块而不是“核心”模块中提供拦截器。如果有人有任何其他建议,或同意这种方法,请加入。

标签: angular angular-http-interceptors


【解决方案1】:

为了绕过所有拦截器,我们可以按照@deg 的建议使用 HttpBackend。

在其他情况下,我们可以创建允许我们从拦截器链中排除拦截器的 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')
  }
}

Ng-run Example

请注意,您可以通过创建基类来重用此逻辑:

@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)
  );
}

【讨论】:

    【解决方案2】:

    根据 GitHub 上的 this suggestion,我们实现了一个简单的标头来识别不应拦截的请求。在拦截器中:

    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 })
        ...
    

    请注意,使用此方法,服务,而不是拦截器,正在选择何时应用拦截器的逻辑;这意味着服务必须“了解”应用程序中的拦截器。根据您的用例,最好让拦截器决定何时应用逻辑。

    【讨论】:

    • 好主意,所以实际上使用标头我们可以创建不同类型的拦截器!
    • @AlexandruOlaru 是的,我想您可以根据需要打开和关闭一对。不过这可能会变得相当复杂,您必须谨慎地做出正确的决定,决定是 service 还是 interceptor 应该选择何时应用转换。我们只有一个用于身份验证的拦截器,并从需要获取初始令牌的一项服务中将其关闭。
    【解决方案3】:

    您可以使用 HttpBackend。

    例子:

    import { HttpClient, ..., HttpBackend } from '@angular/common/http';
    
    @Injectable()
    export class TestService {
    
      private httpClient: HttpClient;
    
      constructor( handler: HttpBackend) { 
         this.httpClient = new HttpClient(handler);
      }
    ....
    

    这样服务就不会被AuthInterceptor拦截了。

    【讨论】:

    • @deg 你现在是我的英雄!我在我的代码中使用了 APP_INITIALIZER 和 HTTP_INTERCEPTOR 令牌。我的拦截器正在使用在 App 初始化程序中初始化的值。我的应用初始化程序正在制作一个 http.get,它反过来又击中了拦截器。你不知道我有多高兴看到这个答案!...
    • 正在使用 APP_INITIALIZER 通过 express 端点获取我的 appsettings,但我的 adal 接收器尚未初始化,因此引发了错误。在搜索了大约一个小时后,这解决了我的问题。可以很容易地看到在此创建博客文章。
    • @rodney 这个解决方案在 github 上得到了极大的支持,核心团队没有反对 github.com/angular/angular/issues/20203#issuecomment-368680437
    • 使用 HttpBackend 绕过所有拦截器,对吗?是否可以排除某些拦截器而不排除其他拦截器?
    • 我正在使用 Angular 9,但仍然是一个有效的解决方案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-23
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 2019-04-17
    • 2018-05-04
    相关资源
    最近更新 更多