Angular: HttpInterceptor, объявленный в библиотечном модуле, перехватывающий запросы вне библиотеки

У меня проблема в Angular, из-за которой реализованный экземпляр HttpInterceptors объявленный в библиотеке Angular перехватывает запросы на вызовы, сделанные вне библиотеки (то есть приложения-потребителя).

Я изо всех сил пытаюсь понять, почему это происходит.

В HTTP_INTERCEPTOR инъекция настраивается в объявлении модуля для моей библиотеки, а не для моего приложения.

Моя установка выглядит следующим образом:

У меня библиотечный модуль объявлен следующим образом:

      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,
        },
      ],
    };
  }
}

Эта библиотека обрабатывает HTTP-запросы для API и добавляет соответствующие заголовки аутентификации, необходимые для связи с указанным API.

Я реализовал HttpInterceptor () и предоставил 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);
      })
    );
  }
}

И, наконец, мои:

      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 class, я ввел две службы, одна из которых моя, а другая - Angular:

      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 быть перехваченным, поскольку это экземпляр, объявленный в ng-mfe, но является экземпляром Angular HttpClient - и HttpClientModule импортируется в AppModule.

Возможно, я неправильно понимаю синглтоны Angular, но почему запросы от http2перехватывают? Моя установка неправильная (и есть ли способ правильно настроить мои модули, чтобы этого не произошло)? Или это правильное и стандартное поведение?

У меня раньше было Injectable({ providedIn: 'root' }) в моем AuthHttpClient а также TokenInterceptor сервис без них заявлен в providers массив в NGMfeModuleно удалили их, поэтому они объявляются локально. Но это не имело никакого значения. Это правильно, или они всегда должны быть providedIn: root?

Спасибо за помощь!

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