可选择在角度4中应用http拦截器

时间:2017-10-15 02:23:20

标签: angular angular-http-interceptors

以下网址解释了如何在Angular 4中使用http拦截器:https://angular.io/guide/http#intercepting-all-requests-or-responses

但是,我想知道是否有办法选择是否使用拦截器?或者选择使用哪组拦截器?我想实现一组身份验证拦截器,但是,当用户访问不需要用户身份验证的loginsignupforget-password等内容时,我不希望应用这些拦截器

如果需要,我只需要在我的服务中注入一个干净的HttpClient实例。我不喜欢我只能使用被所有拦截器污染的HttpClient的单个全局实例的方式。

2 个答案:

答案 0 :(得分:1)

我有相同的要求,并提出了以下解决方案。

在模块中,我使用Token如下提供“ HttpClient”。


    export const HTTP_NOAUTH = new InjectionToken("http_noauth");
    ...
    providers: [...,
    {
        provide: HTTP_NOAUTH,
        deps: [HttpBackend],
        useFactory: (handler: HttpBackend) => {
            return new HttpClient(handler);
        }
    },
    {
        provide: HTTP_INTERCEPTORS,
        useClass: AuthHttpInterceptor,
        multi: true
    }],
    ...

然后,当我注入HttpClient并不想使用AuthHttpInterceptor时,我指定了'@Inject(HTTP_NOAUTH)'。


    @Injectable({
        providedIn: 'root',
    })
    export class SomeService {
        constructor(@Inject(HTTP_NOAUTH) private http: HttpClient) {
        ...

到目前为止,我发现(可能还有更多)的一个主要漏洞是,这是一个全有或全无的解决方案。它要么具有所有拦截器,要么不具有任何拦截器。可能有可能在Token提供的条目中注入单个拦截器,但我还没有深入研究。

更新:

我现在可以如下选择针对HttpClient的每种配置要排除的拦截器。

import { Observable } from 'rxjs';
import { HttpHandler, HttpEvent, HttpRequest, HttpInterceptor, HttpBackend, HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
import { Injector, InjectionToken } from '@angular/core';

export const provideTokenizedHttpClient = (token: InjectionToken<string>, options: { excludes: Function[] } = { excludes: [] }) => {
    return {
        provide: token,
        deps: [HttpBackend, Injector],
        useFactory: (backend: HttpBackend, injector: Injector) => {
            return new HttpClient(
                new HttpDynamicInterceptingHandler(backend, injector, options)
            );
        }
    }
}

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 HttpDynamicInterceptingHandler implements HttpHandler {
    private chain: any = null;

    constructor(private backend: HttpBackend, private injector: Injector, private options: { excludes: Function[] } = { excludes: [] }) { }

    public handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
        if (this.chain === null) {
            const interceptors = this.injector.get(HTTP_INTERCEPTORS, [])
                .filter(entry => !this.options.excludes.includes(entry.constructor));

            this.chain = interceptors.reduceRight((next, interceptor) => {
                return new HttpInterceptorHandler(next, interceptor);
            }, this.backend);
        }
        return this.chain.handle(req);
    }
}

现在在我的提供程序中,我仅使用以下内容:

providers: [...
    provideTokenizedHttpClient(HTTP_NOAUTH, { excludes: [AuthHttpInterceptor] }),
    {
        provide: HTTP_INTERCEPTORS,
        useClass: AppBusyHttpInterceptor,
        multi: true
    },
    {
        provide: HTTP_INTERCEPTORS,
        useClass: AuthHttpInterceptor,
        multi: true
    }],

InjectionToken的创建及其在@Inject装饰器上的用法是相同的。

答案 1 :(得分:0)

我有类似的问题,我花了很多时间来解决它。我想让拦截器层独立于拦截器应用(或不应用)逻辑。

我的解决方案是使分离的模块专用于拦截器。拦截器都不包含if-条件来决定是否应触发。我将此逻辑移至forRoot方法,该方法专用于将附加配置传递给模块。

示例调用:

InterceptorModule.forRoot([
  {
    provide: HTTP_INTERCEPTORS,
    useClass: MenuInterceptor,
    multi: true,
    runConditions: [InterceptConditions.WhiteList],
    whiteList: ['api/menu']
  },
  {
    provide: HTTP_INTERCEPTORS,
    useClass: AuthorizationInterceptor,
    multi: true,
    runConditions: [InterceptConditions.BlackList],
    blackList: ['api/public']
  },
]),

就这些。拦截器包含纯逻辑,forRoot方法包含运行条件。

要使其正常工作,您必须:

  • 添加其他具有封装逻辑的抽象类,以支持黑名单/白名单。每个拦截器都必须扩展它。
  • 使用其他字段重载原始ClassProvider界面

示例:

export interface InterceptorClassProvider extends ClassProvider {
  runConditions: InterceptConditions[];
  whiteList?: string[];
  blackList?: string[];
}
  • 使用forRoot方法加载所有内容

示例:

static forRoot(interceptorProviders: InterceptorClassProvider[]): ModuleWithProviders {
  return {
    ngModule: CwaCoreInterceptorModule,
    providers: [
      { provide: INTERCEPTOR_CONFIG, useValue: interceptorProviders },
      ...interceptorProviders
    ],
  };
}