Sunday, 3 November 2024

Multiple Interceptors in Angular

In Angular, multiple HTTP interceptors can be implemented to handle various aspects of HTTP requests and responses, such as logging, authentication, error handling, and modifying headers. Angular allows multiple interceptors to be defined, and they are executed in the order they are provided in the providers array.

Here’s how to create and configure multiple interceptors in Angular.

Example: Setting Up Multiple Interceptors

Let's assume we want to create two interceptors:

  1. AuthInterceptor: Adds an authorization token to the headers.
  2. LoggingInterceptor: Logs the request details.

Step 1: Create the Interceptors

  1. AuthInterceptor: This interceptor will add an authorization token to outgoing requests.

    typescript
    // auth.interceptor.ts import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const authReq = req.clone({ headers: req.headers.set('Authorization', 'Bearer your-auth-token') }); return next.handle(authReq); } }
  2. LoggingInterceptor: This interceptor logs request information for debugging.

    typescript
    // logging.interceptor.ts import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable() export class LoggingInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { console.log('Request made with url:', req.url); return next.handle(req).pipe( tap(event => { console.log('Response received for:', req.url); }) ); } }

Step 2: Register the Interceptors in the App Module

In the AppModule, provide the interceptors. Note that the order in which they are listed determines the sequence in which they will be executed. Interceptors closer to the top will execute earlier on the way out, and later on the way back in.

typescript
// app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { AuthInterceptor } from './auth.interceptor'; import { LoggingInterceptor } from './logging.interceptor'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true } ], bootstrap: [AppComponent] }) export class AppModule {}

In this configuration:

  • multi: true allows Angular to use multiple interceptors.
  • AuthInterceptor will run first, adding the authorization header.
  • LoggingInterceptor will then log the request and response information.

Important Notes on Interceptor Order

  • Request Chain: Interceptors process requests in the order they are provided in the providers array.
  • Response Chain: Interceptors handle responses in reverse order (last interceptor is the first to handle the response).

Related Interview Questions and Answers

  1. How does Angular determine the order of multiple interceptors?

    • Answer: Interceptors are executed in the order they are listed in the providers array for requests. For responses, they are executed in the reverse order, so the last interceptor added will handle the response first.
  2. What is the purpose of multi: true in the interceptor provider configuration?

    • Answer: multi: true is necessary to allow Angular to use multiple instances of the HTTP_INTERCEPTORS token, enabling multiple interceptors.
  3. Can interceptors modify both requests and responses?

    • Answer: Yes, interceptors can modify both outgoing requests and incoming responses by transforming the request/response objects or adding custom headers, handling errors, or logging data.
  4. What happens if an interceptor modifies the request and another one depends on the original request?

    • Answer: Each interceptor receives the modified request from the previous interceptor. If an interceptor depends on the original request data, you need to ensure it runs before any modifications are made.

In Angular, when using standalone components, interceptors can still be applied, but they need to be configured in the module that provides the HttpClient. Standalone components don’t have a providers array, so the interceptors must be registered in a module or globally for the entire application.

Here's how you can set up interceptors with standalone components:

Step 1: Create the Interceptors

Let’s create two simple interceptors, similar to before.

1. AuthInterceptor - Adds an authorization token.

typescript
// auth.interceptor.ts import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const authToken = 'sample-auth-token'; const authReq = req.clone({ headers: req.headers.set('Authorization', `Bearer ${authToken}`) }); return next.handle(authReq); } }

2. LoggingInterceptor - Logs requests and responses.

typescript
// logging.interceptor.ts import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable() export class LoggingInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { console.log(`Request to ${req.url} with method ${req.method}`); return next.handle(req).pipe( tap(event => { console.log(`Response received from ${req.url}`); }) ); } }

Step 2: Register Interceptors in bootstrapApplication

To use interceptors in standalone components, register them globally in the bootstrapApplication function in the main.ts file. This way, they are applied to all HTTP requests across the application, including in standalone components.

typescript
// main.ts import { enableProdMode, importProvidersFrom } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { AppComponent } from './app/app.component'; import { AuthInterceptor } from './app/auth.interceptor'; import { LoggingInterceptor } from './app/logging.interceptor'; if (environment.production) { enableProdMode(); } bootstrapApplication(AppComponent, { providers: [ provideHttpClient( withInterceptors([ AuthInterceptor, LoggingInterceptor ]) ) ] }).catch(err => console.error(err));

In the above code:

  • We use provideHttpClient with withInterceptors to provide the AuthInterceptor and LoggingInterceptor.
  • withInterceptors registers multiple interceptors, ensuring they apply to all HTTP requests in the application, including any standalone components.

Step 3: Create a Standalone Component with HTTP Requests

Now, let’s create a simple standalone component that makes an HTTP request, which will be intercepted by our global interceptors.

typescript
// app.component.ts import { Component, inject } from '@angular/core'; import { HttpClientModule, HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Component({ selector: 'app-root', template: `<h1>Standalone Component with Interceptors</h1>`, standalone: true, imports: [HttpClientModule] }) export class AppComponent { private http = inject(HttpClient); ngOnInit() { this.getData().subscribe(response => { console.log('Data received:', response); }); } getData(): Observable<any> { return this.http.get('https://jsonplaceholder.typicode.com/todos/1'); } }

Explanation

  1. Global Interceptors: The interceptors are globally registered, so they apply to all HTTP requests in the app, including those from standalone components.
  2. Standalone Component HTTP Requests: The AppComponent makes an HTTP GET request, which will go through the AuthInterceptor and LoggingInterceptor.

Console Output Example

If everything is set up correctly, you should see output similar to:

sql
Request to https://jsonplaceholder.typicode.com/todos/1 with method GET Response received from https://jsonplaceholder.typicode.com/todos/1 Data received: { userId: 1, id: 1, title: "delectus aut autem", completed: false }

By setting up interceptors globally using provideHttpClient with withInterceptors in bootstrapApplication, all HTTP requests, including those in standalone components, will be intercepted, providing consistent functionality across the application.

Share:

0 comments:

Post a Comment