Angular 为不同的服务提供不同的HTTP_拦截器

Angular 为不同的服务提供不同的HTTP_拦截器,angular,Angular,我有一个非常实用的问题,可能有一些奇特的角度依赖注入解决方案 基本上: 我有一个拦截器A来提供一个用于与服务A进行通信的身份验证头 我有拦截器B来提供一个Auth头,用于与服务B通信 如何使服务A的每个实例都具有带有拦截器A的拦截器链?我如何保证服务B是相反的 这两项服务在整个应用程序中都是全局使用的,因此我认为我不能仅在使用服务B/a的模块中定义新的HTTP_拦截器注入令牌 谢谢大家! 考虑到这两个服务都在根注入器上注册,唯一的方法是创建一个新模块,该模块类似于但具有新的DI令牌-HttpCl

我有一个非常实用的问题,可能有一些奇特的角度依赖注入解决方案

基本上:

我有一个拦截器A来提供一个用于与服务A进行通信的身份验证头

我有拦截器B来提供一个Auth头,用于与服务B通信

如何使服务A的每个实例都具有带有拦截器A的拦截器链?我如何保证服务B是相反的

这两项服务在整个应用程序中都是全局使用的,因此我认为我不能仅在使用服务B/a的模块中定义新的HTTP_拦截器注入令牌


谢谢大家!

考虑到这两个服务都在根注入器上注册,唯一的方法是创建一个新模块,该模块类似于但具有新的DI令牌-
HttpClientB
而不是
HttpClient
HTTP\u拦截器
而不是
HTTP\u拦截器
。这需要从
common/http/src
中提取几个内部构件,因为它们没有导出,例如
interceptingHandler

通过这种方式,服务B可以在
HTTP_拦截器中拥有自己的注入器,并将HTTP客户端作为
HttpClientB
注入

一个更简单的方法是拥有一个拦截器,它能够根据输入在实现之间切换。如图所示,在请求期间与拦截器交互的唯一方法是通过请求参数或头传递数据

因此,服务A可以执行以下请求:

http.get(..., { headers: new HttpHeaders({ useAuth: 'A' }) });
拦截器接收到报头并根据其做出决定:

  intercept(req, next) {
    if (req.headers.get('useAuth') === 'A') ...
    else if (req.headers.get('useAuth') === 'B') ...
    else ...

    req.headers = req.headers.delete('useAuth'));
  }

如果拦截器A和B只针对服务A和B,而不针对应用程序的其余部分,则根本不应使用拦截器。相反,这应该直接在服务A和B中完成。

以防将来有人遇到此问题,这就是我最后要做的

我需要向HttpRequest添加静态头,但是这个解决方案可以很容易地修改以允许更多的动态头

我制作了一个属性添加拦截器,如下所示:

attribute.interceptor.ts

@Injectable()
export class AttributesInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    const attributes = environment.attributes;
    let attributeReq = req;
    for (let i = 0; i !== attributes.length; i++) {
      const attribute = attributes[i];
      if (this.urlMatches(attribute.urls, req)) {
        // The url matches so let's apply the attribute
        attributeReq = attributeReq.clone({ headers: req.headers.set(attribute.header, attribute.value) });
      }
    }

    return next.handle(attributeReq);
  }

  urlMatches(urls: string[], req: HttpRequest<any>): boolean {
    for (let i = 0; i !== urls.length; i++) {
      if (req.url.includes(urls[i])) {
        return true;
      }
    }

    return false;
  }
}
export const endpoints = {
  midway: 'http://localhost:8080',
  builds: 'http://localhost:7245'
};

export const environment = {
  production: false,
  logLevel: LogSeverity.INFO,
  endpoints: endpoints,
  attributes: <Attribute[]> [
    {
      header: 'X-COMPANY',
      value: 'SECRET-TOKEN',
      urls: [ endpoints.midway ]
    }
  ]
};
因此,现在我可以向任何服务调用添加特定的静态头,只需使用服务基url和头/值附加this属性数组


这个解决方案可以扩展到更复杂的用例,但这个简单的解决方案正是我所需要的

拦截器是单个到整个应用程序的。在拦截器内部也可以这样做。根据你的情况,你可以在你的拦截器中操作数据,这有点不好。这意味着,如果您使用自己的拦截器导入一个API客户机,它将添加到应用程序拦截器上,而应用程序拦截器最终将扩展到许多拦截器。我可以想象,有某种方法可以基于您可以在客户端API的构造函数中指定的某个令牌请求不同的HTTP_拦截器链。或者一些特殊的拦截器从请求中读取一个字符串并应用一组不同的拦截器。基本上必须在拦截器中过滤我想要应用的请求,这是非常讨厌的。我想我可以创建一些基类。但它仍然不是最优的。同意!但这不是一种反模式吗?我想知道angular开发者在angular 4中重新发明Http客户端时,没有考虑到一个应用程序有多于一个的API后端有不同的要求。我把这个标记为答案,因为这基本上是我最后做的,虽然它看起来像一个反模式,因为拦截器意味着将逻辑从特定的HTTP实现解耦,但是你必须在这里重新添加一些检查逻辑。解耦逻辑应该从拦截器转移到服务。拦截器应该提供全局行为。如果它不应该是全局的,那么这就不是拦截器的用例。如果只有两种服务需要它,它真的应该是全球性的吗?如果你认为应该的话,我建议在A4回购中就你的案例提出一个问题。无论如何,你可以用另一个HttpClient实现创建一个新的模块,它将不同于默认的模块,正如答案中提到的那样。它将拥有自己的拦截器。如果您需要解耦的HttpClient逻辑,这就是方法。拦截器的“全局”含义显著降低了其可用性。@AlexChe global Behavior是拦截器的用例。如果您不需要在全球范围内应用它们,那么您可能根本不需要它们。如果您遇到与此问题类似的问题,另一种选择是使用不同的HttpClient实例,其中一个实例有拦截器,另一个实例没有拦截器。这可能很棘手,因为Angular不支持开箱即用,但肯定是可能的。
export class Attribute {
  header: string;
  value: string;
  urls: string[];
}