Exploring Angular 4.3 Interceptors: A Practical Guide

I am currently working on a new app that needs authorization headers. Normally, I follow a similar approach to what is described in this article on scotch.io. However, I have recently learned that Angular 4 now fully supports HTTP Interceptors through the new HttpClientModule and I am looking for documentation on how to use them effectively.

If my assumption about this method being the best practice for injecting authorization headers in Angular 4 (as of version 4.3) is incorrect, I am open to suggestions. I believe it is a newly added feature, so migrating to an "Angular Approved" method seems like a logical step forward.

Answer №1

This response is drawing on information from the official documentation referenced by CodeWarrior.

Angular provides the capability to create an HttpInterceptor:

import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';

@Injectable()
export class NoopInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req);
  }
}

You can then incorporate this interceptor into your application as shown below:

import {NgModule} from '@angular/core';
import {HTTP_INTERCEPTORS} from '@angular/common/http';

@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: NoopInterceptor,
    multi: true,
  }],
})
export class AppModule {}

If you need to include an authorization header, you can duplicate the request with modified headers:

import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private auth: AuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Retrieve the authentication header from the service.
    const authHeader = this.auth.getAuthorizationHeader();
    // Clone the request to add the new header.
    const authReq = req.clone({headers: req.headers.set('Authorization', authHeader)});
    // Proceed with the duplicated request instead of the original one.
    return next.handle(authReq);
  }
}

Remember that these interceptors function in a sequential manner, allowing you to configure multiple interceptors for various tasks.

Answer №2

Encountered an issue when trying to inject AuthService into the Interceptor's constructor, resulting in this error message:

Uncaught Error: Provider parse errors: Cannot instantiate cyclic dependency! InjectionToken_HTTP_INTERCEPTORS ("[ERROR ->]"): in NgModule AppModule in ./AppModule@-1:-1

To resolve the problem, I opted to utilize the Injector from @angular/core, which successfully resolved the issue. The token is stored in localStorage and basic auth is being used. Below is the implementation:

Authorization: 'Bearer token_string'

This is how it was implemented:

token.interceptor.ts

import {Injectable, Injector} from '@angular/core';

import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {AuthService} from './auth.service';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {

    constructor(private injector: Injector) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        const auth = this.injector.get(AuthService);
        if (auth.getToken()) {
            request = request.clone({
                setHeaders: {
                    Authorization: `Bearer ${auth.getToken()}`
                }
            });

        }

        return next.handle(request);
    }
}

getToken function in AuthService

The logic to retrieve the header or just the token can be implemented here. In this case, only the JWT token string is retrieved.

/**
 * Get jwt token
 * @returns {string}
 */
getToken(): string {
    return localStorage.getItem('token');
}

app.module.ts

Import the TokenInterceptor

import {TokenInterceptor} from './pathToTheFile/token.interceptor';

Add the following code snippet under @NgModule within the providers: array.

providers: [
    {
        provide: HTTP_INTERCEPTORS,
        useClass: TokenInterceptor,
        multi: true
    }
    //, other providers
]

Answer №3

The issue I encountered with the suggested method was that the interceptors needed to be predetermined at compile time and confined within the same module.

To address this limitation, I decided to develop a single interceptor along with a series of handler functions that could be expanded dynamically during runtime. The intercept method in the service manages the next() operation.

Below is the bare-bones code for the service (excluding validations or upkeep):

export type HttpHandlerFunction = (req: HttpRequest<any>) => HttpRequest<any>;

@Injectable()
export class HttpInterceptorService implements HttpInterceptor {
    private _handlers: Array<HttpHandlerFunction> = [];

    addHandler(handler: HttpHandlerFunction): void {
        this._handlers.push(handler);
    }

    intercept(req: HttpRequest<any>, next: HttpHandler):
        Observable<HttpEvent<any>> {
            this._handlers.forEach((handler: HttpHandlerFunction) => {
                req = handler(req);
            })
        return next.handle(req);
    }
}

Here's how you can utilize it in a different module within a service:

constructor(injector: Injector) {

    const interceptorsArray: Array<any> = injector.get(HTTP_INTERCEPTORS),
        interceptor: HttpInterceptorService = interceptorsArray &&
            interceptorsArray.filter((i: any) => i instanceof HttpInterceptorService)[0];
    if (interceptor) {
        interceptor.addHandler((req: HttpRequest<any>) => {
            const accessToken = this.getAccessToken();
            if (accessToken) {
                // direct headers.set doesn't function correctly, must clone
                req = req.clone({ headers: req.headers.set('Authorization', accessToken) });
            }
        return req;
    });
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Struggling with incorporating GlobalStyles in the app.tsx file

I have been working with next13 and styled-components. Initially, everything seemed fine in my file globalStyles.ts, and all was functioning perfectly. However, I started encountering errors related to the import of <GlobalStyles/>. Specifically, th ...

In Angular 2, if one HTTP call is dependent on another, ensure to retry all calls if one fails for seamless operation

I have a situation with this Angular 2 code where it first makes an http call to './customer.json' and then uses the returned URL to make another call. I want to implement retry functionality for both calls using the rxjs retry method. Currently, ...

Getting the item that was clicked on a Chart in a PrimeNG chart within an Angular application can be achieved by following these

I am trying to implement a bubble chart and I would like the function to be called when a user clicks on one of the bubbles. What is the best way for me to pass the data to this function? https://i.stack.imgur.com/FYiSP.png <p-chart type="bubble" [da ...

Angular 16 brings a revolution in routerLink behavior

Previously, when I was using angular < 16, my routes configuration looked like this: { path: Section.Security, canActivate: [AuthGuard, AccessGuard, AdminGuard], children: [ { path: '', pathMatch: 'full', ...

In Angular, the data retrieved from the API will only appear on the page once it has been manually

Recently, I started working on an Angular project and encountered a problem with data display after each component routing. Initially, the data is not displayed until the page is reloaded. You can see the issue in the screenshots: https://i.sstatic.net/5My ...

Sharing data between sibling components becomes necessary when they are required to utilize the *ngIf directive simultaneously

Within my parent component, I am hosting 2 sibling components in the following manner: <div *ngif="somecode()"> <sibling1> </sibling1> </div> <div *ngif="somecode()"> <sibling1 [dataParams]=sibling1object.somedata> ...

Issue: ng test encountered a StaticInjectorError related to FormBuilder

I recently started using ng test to run tests on my Angular application. I utilized the Angular-Cli tool to create modules and components, and the .spec.ts files were generated automatically. During one of the tests, I encountered the following error: ...

Showing JSON object in an Angular 2 template展示JSON对象在模

When I execute the following code: stanservice.categoryDetail(this.params.get('id')) .then((data) => { this.category = JSON.stringify(data.res.rows[0]); console.log(JSON.stringify(data.res.rows[0])); }) .catch((error) => { ...

Solving runtime JavaScript attribute issues by deciphering TypeScript compiler notifications

Here is a code snippet I am currently working with: <div class="authentication-validation-message-container"> <ng-container *ngIf="email.invalid && (email.dirty || email.touched)"> <div class="validation-error-message" *ngIf=" ...

Determine whether the specified date falls on a public holiday within the selected

I'm currently working with Angular 12 and I need to determine whether today is a regular workday or a day off for employees, including weekends and public holidays. I attempted to use the date-holidays package by importing it like this: import Holida ...

Error: The attribute 'detect' is not a valid property of object 'GraphModel'

I am currently experimenting with using TensorFlow JS to develop a real-time object detection application in Angular 13. My goal is to utilize a video element that captures footage from the webcam, and I am attempting to invoke the model.detect(video) meth ...

The modification in Typescript's type order from version 1.7 to 1.8 resulted in a significant

A Visual Studio Cordova application with a unique TypeScript source structure: /src /app /appsub1 appsub1.ts -> 4 : 7 /appsub2 appsub2.ts -> 5 : 6 app.ts -> 3 : 5 /mod1 /mod1sub1 mod1sub1.ts -> 7 : 4 m ...

Get the file without specifying type in request - Angular

Is it possible to download a file solely through the response without specifying a responsetype in the header? I am looking for a way to download the file without including any additional parameters in the header. ...

Angular 6 form controls with reactive elements

Looking to create a simple homepage using Angular 6. One of the features will include tests for prime factorization and leap years, implemented with reactive forms for validation. However, I am facing an issue where I cannot execute both functions simultan ...

The property you are trying to access is not found within the declared type of 'IntrinsicAttributes & { children?: ReactNode; }'

In my React project created using Create-React-App, I have included the following packages (relevant to the issue at hand): "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "^5.1.2", "react-scripts": "3.4.1", "typescript": "^3.9.2", "@typ ...

An Angular CDK overlay conflict occurring within a nested component

Incorporating the Angular CDK overlay into my project, I've successfully implemented a modal drawer and tooltips. However, I've encountered an issue when trying to close the drawer while a tooltip is still active within it. Upon pressing Escape ...

When trying to use ngModel with Angular, an error occurs where the property 'selected' cannot be created on a string

I'm currently attempting to utilize [(ngModel)] in the following manner: <div class="items"> <tbody> <tr *ngFor="let info of this.information"> <td> <input type="checkbox" [(ngModel)]="this.info.n ...

TS fails to recognize any additional properties added to the constant object

When working on a function that should return an object with properties 'a' and 'b', I am defining the object first and then adding values to it later: const result = {}; result.a = 1; result.b = 2; return result; However, TypeScript i ...

Angular2's AngularFire2: Issue with Nested Observables not Displaying in the View

I am currently working on an experimental app using Ionic 2, Firebase, and AngularFire2 (which is still in alpha). I have been following a tutorial by Aaron Saunders as a foundation for my project: https://github.com/aaronksaunders/ionic2-angularfire-sam ...

Angular4 ChromeDriver Selenium Protractor

I am facing an issue while trying to run 'ng e2e'. The error message I encounter is as follows: WebDriverError: unknown error: cannot find Chrome binary I am using Protractor which is pre-installed with Angular CLI. Despite reinstalling ChromeD ...