【问题标题】:Interceptors only for specific Service仅针对特定服务的拦截器
【发布时间】:2017-09-09 12:34:38
【问题描述】:

我的应用中有多个服务指向不同的 API URL。现在我需要为这些服务中的每一个设置不同的标头。我现在的问题是关于 Angular 4 中的新拦截器。是否有可能为特定服务设置一个拦截器?那么每个服务都有其特定的拦截器?

希望你们能明白我的问题。

【问题讨论】:

    标签: angular4-httpclient


    【解决方案1】:

    TL:DR 答案:

    不,没有办法。拦截器旨在拦截所有请求。

    长答案:

    存储库或请求不应该知道它可以通过的拦截器。因此,我不同意标记请求或检查特定类的解决方案。

    我更喜欢这里提供的解决方案: Example Angular HttpInterceptors

    基本上,如果应添加特定标头,您的拦截器必须检查服务(中介模式)。

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        console.log(JSON.stringify(req));
    
        const token: string = this.currentUserService.token;
    
        if (token) {
            req = req.clone({ headers: req.headers.set('Authorization', 'Bearer ' + token) });
        }
    
        if (!req.headers.has('Content-Type')) {
            req = req.clone({ headers: req.headers.set('Content-Type', 'application/json') });
        }
    
        req = req.clone({ headers: req.headers.set('Accept', 'application/json') });
        return next.handle(req);
    }
    

    这是一个很好的例子,但要注意它违反了单一职责原则(设置多个标题)。

    除此之外,我认为拦截器对于您的问题来说是错误的模式。此外,我没有将拦截器视为向请求添加不记名令牌的解决方案。那是我的用例,它把我带到了这里。

    基本上,我会挑战您的架构并重新考虑您如何创建请求。这个问题的解决方案可能是以下设计:

    抽象存储库

    具有 get / post / put 等基本方法,返回 HttpRequest。

    有一个名为“send”的方法,它接受一个 HttpRequest 作为参数。

    具体存储库

    从抽象存储库继承并扩展基本请求功能。

    因此,对于您的用例,您有一个基本服务,并且每个特定/自定义服务都继承自该特定服务,从而扩展了您的请求行为。

    装饰器

    为了使这种架构更进一步(就像我所做的那样),您可以创建一个 typescript 装饰器(并非在所有情况下都可行,例如当需要依赖注入时),它扩展了所有装饰函数的行为。例如添加特定的标题。这可能如下所示:

    import { Observable } from 'rxjs/Observable';
    import { HttpClient, HttpRequest, HttpEvent } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    
    export abstract class BaseRepository<T> {
        constructor(protected client: HttpClient) {
    
        }
    
        public createGetRequest(url: string): HttpRequest<T> {
            return new HttpRequest("GET", url);
        }
    
        public send(request: HttpRequest<T>): Observable<HttpEvent<T>> {
            return this.client.request(request);
        }
    }
    
    @Injectable()
    export class NormalServiceRepository extends BaseRepository<any> {
    
        constructor(protected client: HttpClient) {
            super(client);
        }
    
        public get(url: string): Observable<any> {
            const baseRequest = super.createGetRequest(url);
    
            baseRequest.headers.set('Content-Type', 'application/json');
    
            return this.send(baseRequest);
        }
    }
    
    
    
    @Injectable()
    export class AuthServiceRepository extends BaseRepository<any> {
    
        constructor(protected client: HttpClient) {
            super(client);
        }
    
        @AcceptsJson()
        @SendsJson()
        @ForwardCredentials()
        public createGetRequest(url: string): HttpRequest<any> {
            return super.createGetRequest(url);
        }
    
        public get(url: string): Observable<any> {
            const baseRequest = super.createGetRequest(url);
            return this.send(baseRequest);
        }
    }

    这应该可以让您大致了解架构的外观。

    关于装饰器的更多信息

    TypeScript Decorators

    Example Implementations

    【讨论】:

      【解决方案2】:

      实际上有一种方法可以为特定服务提供一个拦截器。请注意重点,因为它实际上并不是 HttpInterceptor 接口的实现。

      TL:DR - 跳转到底部的示例。

      HttpClient 实际上只通过所有可能的方法将输入转换为称为HttpRequest 的单个结构,然后将其传递给HttpHandler 实现。顾名思义,它负责处理 HTTP 请求。
      HttpHandler 实现中,您会发现例如HttpInterceptingHandler(运行拦截器链)或 HttpXhrBackend(通过 HttpBackend 类继承,此处理程序是最后一个,因为它实际发送 XHR 请求) .如果您查看the source code of the former class,您会发现它实际上取决于后者(间接地,在HttpBackend 令牌下,HttpXhrBackend 作为默认实现提供)。所以就像拦截器一样,Angular 通过 DI 链接处理程序,其中最后一个是执行请求的后端。

      您可以做的是在这个处理程序链中添加另一个处理程序,最好是作为第一个处理程序。为此,您必须定义一个扩展HttpHandler 的类,注入HttpHandlerfirst 实现(首先我指的是在HttpHandler 令牌下提供的实现)然后调用- 完成后通过handle 方法。

      最后,由于 HTTP 客户端是一个 Angular 服务,它可能会在更多服务之间共享,因此您需要创建自己的实例来避免这种情况。 HttpClient 构造函数需要一个 HttpHandler 实例,您可以在其中传递您自己的实现。

      请参阅我在此类处理程序中处理身份验证的简单示例:

      @Injectable()
      export class AuthHandler extends HttpHandler {
          constructor(
              private readonly auth: AuthService,  // custom service providing the auth token
              private readonly next: HttpHandler   // injects the "default" handler -> HttpInterceptingHandler
          ) {
              super();
          }
      
          /** @override */ handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
              // do whatever you need on the request
              // because of immutability cloning is required
              const clone = req.clone({
                  setHeaders: { 'Authorization': 'Bearer ' + auth.getToken() }
              });
              // pass the request to the next handler, eventually ending up in HttpXhrBackend
              return this.next.handle(clone);
          }
      }
      

      以及该处理程序在该特定服务中的用法:

      export class HeroService {
          protected readonly http: HttpClient;
      
          constructor(auth: AuthHandler) {
              // create your own instance with you custom handler
              this.http = new HttpClient(auth);
          }
      
          async getHero(id: string): Hero {
              // every request made through such client goes to the handler for processing
              await this.http.get<Hero>(`/api/heroes/${id}`).toPromise();
          }
      }
      

      此代码在 Angular 8 上为我工作。
      我希望这可以帮助其他迷失的灵魂寻找解决方案。

      【讨论】:

      • 另外值得一提的是,这也是可测试的,因为只有最后一层 (HttpXhrBackend) 被替换为测试类。我刚刚在我的代码中验证了它。
      【解决方案3】:

      一旦使用新的 HttpClient 触发请求/响应,将调用 所有 拦截器。您可以做的一件事是标记您的请求,因此您可以在旨在处理该请求的拦截器中设置正确的标头。

      【讨论】:

      • 感谢您的回答。我如何标记我的请求?感谢您的帮助。
      • 您可以例如创建单独的请求类型类,扩展 HttpRequest,以便您可以按类类型进行过滤。您可以添加一个特殊的 Header 来过滤。您可以在 URL 的末尾添加一个井号标签。只是一些例子。
      • 但是如何识别对此类标记请求的响应?
      【解决方案4】:

      这个问题有点晚了,但这对我们有用。是的,它进入拦截器,但一个简单的条件可以使它不执行。

       intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
          // Do not execute interceptor
          if (some condition)
            return next.handle(req);
      
          // Else.. do your thing and go next...
          // Add headers, modify reqyest, etc..
          return next.handle(req);
        }
      

      【讨论】:

        猜你喜欢
        • 2013-10-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-30
        • 2020-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多