【问题标题】:Angular: HttpInterceptor declared in library module intercepting requests outside of libraryAngular:在库模块中声明的 HttpInterceptor 拦截库外的请求
【发布时间】:2021-08-12 08:12:54
【问题描述】:

我在 Angular 中遇到一个问题,即在 Angular 库中声明的 HttpInterceptors 的实现实例正在拦截在库外部(即消费应用程序)发出的 HttpClient 调用请求。

我很难理解为什么会这样。

HTTP_INTERCEPTOR 注入是在我的库而不是我的应用程序的模块声明中配置的。

我的设置如下:

我的 ng-mfe 库模块声明如下:

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { ModuleWithProviders, NgModule } from '@angular/core';

import { CommonModule } from '@angular/common';
import { ConfigToken } from './tokens';
import { AuthHttpClient } from './services/auth/auth-http.service';
import { Config } from './models/module.model';
import { NgSelectModule } from '@ng-select/ng-select';
import { TokenInterceptor } from './services/auth/token.interceptor';

const components = [/* components */];
const imports = [CommonModule, HttpClientModule, NgSelectModule, FormsModule, ReactiveFormsModule];

@NgModule({
  declarations: [...components],
  imports: [...imports],
  exports: [...components],
  providers: [],
})
export class NGMfeModule {
  static forRoot(config: Config): ModuleWithProviders<NGMfeModule> {
    return {
      ngModule: NGMfeModule,
      providers: [
        AuthHttpClient,
        TokenInterceptor,
        {
          provide: ConfigToken,
          useValue: config,
        },
        {
          provide: HTTP_INTERCEPTORS,
          useClass: TokenInterceptor,
          multi: true,
        },
      ],
    };
  }
}

此库处理 API 的 HTTP 请求并附加与所述 API 通信所需的相关身份验证标头。

我已经实现了HttpInterceptor (TokenInterceptor) 并提供了HTTP_INTERCEPTORS 注入令牌。

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { filter, map, switchMap } from 'rxjs/operators';
import { selectAccessToken } '../../state/shared-config/shared-config.selectors';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  constructor(private store: Store) {}

  /**
   * Intercept all HTTP requests and add the required auth headers to them
   *
   * @param req The request to intercept
   * @param next Transform HttpRequests into a stream
   */
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.store.select(selectAccessToken).pipe(
      filter((data) => !!data),
      switchMap((data) => {
        req = req.clone({
          setHeaders: {
            Authorization: `Bearer ${data}`,
          },
        });

        return next.handle(req);
      })
    );
  }
}

最后,我的AuthHttpClient

import { HttpClient } from '@angular/common/http';
import { HttpOptions } from '../../models/http.model';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable()
export class AuthHttpClient {
  constructor(private http: HttpClient) {}

  get(url: string, options?: HttpOptions): Observable<Object> {
    return this.http.get(url, options);
  }
}

这是库配置,我在库之外的 Angular 应用程序模块是 如下:

import { NGMfeModule } from 'ng-mfe';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    NGMfeModule.forRoot(/* config */),
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

最后,在我的AppComponent 类中,我注入了两个服务,一个是我的AuthHttpClient,另一个是Angular 的HttpClient

import { Component, OnInit } from '@angular/core';
import { AuthHttpClient, getAccessToken } from 'ng-mfe';

import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
  constructor(
    private http1: AuthHttpClient,
    private http2: HttpClient
  ) {}

  ngOnInit() {
    this.http1.get('test-url-1').subscribe();
    this.http2.get('test-url-2').subscribe();
  }
}

我希望http1 的调用会被拦截,因为这是AuthHttpClient 的一个实例,它在ng-mfe 中声明,但http2 是Angular 的HttpClient 的一个实例 - 和@987654342 @在AppModule中导入。

也许我对 Angular 单例的理解是错误的——但为什么来自http2 的请求会被拦截?我的设置是否错误(有没有办法正确配置我的模块,这样就不会发生)?还是这是正确的标准行为?

我之前在我的AuthHttpClientTokenInterceptor 服务中有Injectable({ providedIn: 'root' }),但没有在NGMfeModuleproviders 数组中声明它们,但删除了这些,以便在本地声明它们。但它没有任何区别。这是正确的,还是应该总是providedIn: root

感谢您的帮助!

【问题讨论】:

    标签: angular dependency-injection angular-httpclient angular-http-interceptors injection-tokens


    【解决方案1】:

    您的问题有两种解决方案:

    1. 您可以提供自己的 HttpClient 实现并注入 你的 HTTP 拦截器,如下所示:https://indepth.dev/posts/1455/how-to-split-http-interceptors-between-multiple-backends

    2. Angular 12 现在有HttpContextTokens。有了这些你 可以有条件地将您的逻辑应用到您的 HTTP 拦截器中 请求中存在特定令牌: https://netbasal.com/new-in-angular-v12-passing-context-to-http-interceptors-308a1ca2f3dd

    【讨论】:

      猜你喜欢
      • 2021-02-09
      • 2018-08-20
      • 2018-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-04
      • 2018-05-04
      • 2015-02-01
      相关资源
      最近更新 更多