仅针对特定服务的拦截器

Tom*_*Tom 6 angular4-httpclient

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

希望你们能回答我的问题。

Nig*_*ing 8

TL:DR答案:

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

长答案:

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

我更喜欢这里提供的解决方案: 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);
}
Run Code Online (Sandbox Code Playgroud)

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

除此之外,我认为拦截器是您问题的错误模式。另外,我也没有将拦截器作为向请求添加承载令牌的解决方案。那是我的用例,使我来到这里。

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

抽象资料库

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

有一个称为“发送”的方法,该方法接受HttpRequest作为参数。

混凝土储存库

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

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

装饰器

为了使该体系结构更进一步(如我所做的那样),您可以创建一个Typescript装饰器(并非在所有情况下都是可能的,例如,当需要依赖注入时),它可以扩展所有装饰函数的行为。例如,添加特定的标头。可能看起来像这样:

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);
}
Run Code Online (Sandbox Code Playgroud)

这应该给您基本构架架构的外观。

有关装饰器的更多信息

TypeScript装饰器

示例实现


ink*_*sso 6

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

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

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

您可以做的就是向此处理程序链添加另一个处理程序,最好是作为第一个处理程序。为此,您必须定义一个扩展类HttpHandler,注入第一个实现HttpHandler(首先我指的是在令牌下提供的实现),并在完成后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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这段代码在 Angular 8 上适用于我。
我希望这可以帮助其他迷失的灵魂寻找解决方案。

编辑2023-06-20:修复了 GitHub 中 Angular 代码的链接。


小智 1

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