[HTTP request in Angular] - Interceptor HttpInterceptor Detailed Explanation

        By studying the working mechanism of HttpClient, we know that for HTTP requests, HttpClient actually builds a chained processing flow:

        The request is made during the processing flow of HttpBackend. Multiple interceptors can be set in front of HttpBackend to process the request.

        For a detailed description of HttpClient, please refer to: HttpClient Request Details in Angular

1. Write an interceptor

        To implement an interceptor, implement a class that implements the intercept() method in the HttpInterceptor interface.

        The following code implements an interceptor that does nothing but add printing:

import { Injectable } from "@angular/core";
import { HttpInterceptor, HttpHandler, HttpRequest, HttpEvent } from '@angular/common/http'
import { Observable } from "rxjs";
import { mergeMap } from "rxjs/operators";
 
@Injectable()
export class InterceptorA implements HttpInterceptor {
 
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        req = this.handleRequest(req);
        return next.handle(req).pipe(
            mergeMap(evt => this.handleResponse(evt))
        );
    }
 
    /**
     * 请求参数拦截处理
     */
    handleRequest(req: any) {
        console.log(`拦截器A在请求发起前的拦截处理`);
        return req;
    }
    
    /**
     * 返回结果拦截处理
     */
    handleResponse(evt: any) {
        console.log("拦截器A在数据返回后的拦截处理");
        return new Observable<HttpEvent<any>>(observer => {
            observer.next(evt);
        });
    }
}

In actual projects, you can add the desired interception processing logic in the above handleRequest() method and handleResponse() method         according to your requirements .

        The next object in the intercept() method represents the next interceptor in the interceptor linked list, and the chain call effect is achieved by calling next.handle() . The last next object in this linked list is HttpClient's backend processor (HttpBackend), which will send the request to the server and receive the server's response.

2. Register to provide interceptor

        This InterceptorA is a service managed by Angular's Dependency Injection (DI) system. Like other services, you must provide this interceptor class before the program can use it.

        Since interceptors are an optional dependency of the HttpClient service, you must register to provide these interceptors in the same injector that provides HttpClient (or its parent injector at all levels). Since HttpClientModule is imported in AppModule, this application provides HttpClient in its root injector. So also register and provide these interceptors in AppModule.

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { InterceptorA } from './core/kits/interceptor-a';
 
@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    HttpClientModule,
    AppRoutingModule
  ],
  providers: [{ provide: HTTP_INTERCEPTORS, useClass: InterceptorA, multi: true }],   // InterceptorA 注册语句
  bootstrap: [AppComponent]
})
export class AppModule { }

        Note the multi: true option. This required option tells Angular that HTTP_INTERCEPTORS is a multi-provider token, meaning it will inject an array of multiple values ​​instead of a single value.

3. Interceptor effect

Call the HttpClient get() method         in the component , taking CSDN to obtain the list of popular search keywords as an example:

    let url = `https://silkroad.csdn.net/api/v2/assemble/list/channel/search_hot_word?channel_name=pc_hot_word&size=10&platform=pc`;
    console.log(`组件调用请求方法`);
    this.http.get(url).subscribe(data => {
      console.log("组件拿到请求返回数据");
    });

        Debugging the program, the page is printed as follows:

        It is found by printing that the interception processing after the data is returned is triggered twice. This is because under normal circumstances, two data notifications are sent out during the processing of HttpBackend.

        Once the notification of the status of the request was issued immediately after the request was sent:

        The data emitted here is { type: 0 } .

         (Tracking code definition can know HttpEventType[HttpEventType["Sent"] = 0] )

        Another time is in the onLoad  event of XMLHttpRequest:

        The data sent here is an HttpResponse object.

        According to the values ​​sent twice, adjust the InterceptorA code of the interceptor to distinguish between the sending status and the returned data:

    /**
     * 返回结果拦截处理
     */
    handleResponse(evt: any) {
        return new Observable<HttpEvent<any>>(observer => {
            if (evt instanceof HttpResponse) {
                console.log("拦截器A在数据返回后的拦截处理");
            } else {
                console.log(`拦截器A接收到请求发出状态:${JSON.stringify(evt)}`);
            }
            observer.next(evt);
        });
    }

        The adjusted intercept print is shown below:

 4. Multiple interceptors

        Add another interceptor B according to the appeal process to check the interception effect.

Add the registration statement of InterceptorB         in AppModule :

  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: InterceptorA, multi: true },
    { provide: HTTP_INTERCEPTORS, useClass: InterceptorB, multi: true },
  ],

        Debugging and running, the page is printed as follows:

         It can be seen that the interceptors in the lower order will intercept the request parameters later before the request is sent, and the interceptors in the lower order will intercept the returned data first when processing the request return value. The component does not get the returned data until all interceptors are processed.

5. Return data filtering

        It can also be found by printing the information that the interceptors have captured the status information of the request. But the component did not get it, only the returned data was obtained in the component.

        This is because HttpClient filters the data after processing by HttpHandler, and can only return HttpResponse information:

 6. Default interceptor

        Careful students can find that in the actual program, there is one more interceptor than the registered one ( HttpXsrfInterceptor ):

         Looking at the code, you can find that HttpXsrfInterceptor is registered in HttpClientXsrfModule :

         Then HttpClientModule introduces HttpClientXsrfModule:

Because HttpClientModule is introduced in AppModule, the interceptor HttpXsrfInterceptor         is actually enabled by default in the program   .

        Check out the HttpXsrfInterceptor definition:

/**
 * `HttpInterceptor` which adds an XSRF token to eligible outgoing requests.
 */
class HttpXsrfInterceptor {
    constructor(tokenService, headerName) {
        this.tokenService = tokenService;
        this.headerName = headerName;
    }
    intercept(req, next) {
        const lcUrl = req.url.toLowerCase();
        // Skip both non-mutating requests and absolute URLs.
        // Non-mutating requests don't require a token, and absolute URLs require special handling
        // anyway as the cookie set
        // on our origin is not the same as the token expected by another origin.
        if (req.method === 'GET' || req.method === 'HEAD' || lcUrl.startsWith('http://') ||
            lcUrl.startsWith('https://')) {
            return next.handle(req);
        }
        const token = this.tokenService.getToken();
        // Be careful not to overwrite an existing header of the same name.
        if (token !== null && !req.headers.has(this.headerName)) {
            req = req.clone({ headers: req.headers.set(this.headerName, token) });
        }
        return next.handle(req);
    }
}

        The comment states that the HttpInterceptor adds an XSRF token to eligible outgoing requests. Qualified requests here refer to non-mutating requests and absolute URLs. From the code, it seems that it refers to non-GET and HEAD requests of the same origin.

Guess you like

Origin blog.csdn.net/evanyanglibo/article/details/122368884