Angular HttpContext Example

By Arvind Rai, March 24, 2024
Angular provides HttpContext to set and get user values. It is used to pass metadata to interceptors. We can set context values while creating HTTP methods using HttpClient and get those values in interceptors.
Some usabilities of HttpContext are sending different retry count for different HttpClient methods to interceptors, sending a flag to interceptor if this HTTP method is cacheable or not, and so on.
HttpContext is mutable and can be updated with new values. To set and access context, HttpContext uses token as HttpContextToken.

1. HttpContextToken

HttpContextToken creates a token that is used to sore, delete or update a value in HttpContext associated with this token.
Find its constructor.
constructor(defaultValue: () => T) 
Create object of HttpContextToken for boolean.
const MY_CONTEXT_TOKEN = new HttpContextToken<boolean>(() => false); 
For string:
const MY_CONTEXT_TOKEN = new HttpContextToken<string>(() => ""); 
For number:
const MY_CONTEXT_TOKEN = new HttpContextToken<number>(() => 0); 

2. HttpContext

HttpContext class is used to store arbitrary user defined values. It has following methods.
set() : Stores a value in the context for a token.
get() : Retrieves the value for given token.
delete() : Deletes the value associated with a token.
has() : checks if the context has value associated with given token.
keys() : Return iterator for all keys in the context.
const myContext = new HttpContext()
            .set(MY_CONTEXT_TOKEN1, "Bob")
            .set(MY_CONTEXT_TOKEN2, 30); 
HttpContext is configured with HttpClient methods such as get(), post(), put(), delete() etc.
this.http.get("/api/users", {
            params: httpParams,
            context: myContext
        }); 

3. HttpRequest.context

We can fetch the instance of HttpContext from HttpRequest object using HttpRequest.context property in HTTP interceptors.
export const httpInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {
    console.log(req.context.get(MY_CONTEXT_TOKEN1));
    console.log(req.context.get(MY_CONTEXT_TOKEN2));
    ------
}; 

4. Setting HttpContext using HttpClient

Here I will create code to set HttpContext to the methods of HttpClient.
Find the context token initialized with default values.
constants.ts
import { HttpContextToken } from "@angular/common/http";

export const IS_CACHEABLE = new HttpContextToken<boolean>(() => false);
export const RETRY_COUNT = new HttpContextToken<number>(() => 2);
export const IS_AUTH_REQUIRED = new HttpContextToken<boolean>(() => false);
export const HTTP_ERROR_COUNT = new HttpContextToken(() => 0); 
Now find the service class that are setting HTTP context to HTTP methods. These contexts will be accessed in HTTP interceptors.
book.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpContext, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Book } from '../book';
import { IS_AUTH_REQUIRED, IS_CACHEABLE, RETRY_COUNT } from '../constants';

@Injectable({
    providedIn: 'root'
})
export class BookService {
    constructor(private http: HttpClient) { }
    getBook(bid: string): Observable<Book[]> {
        const httpParams = new HttpParams().set('id', bid);
        const myContext = new HttpContext()
            .set(IS_CACHEABLE, true)
            .set(RETRY_COUNT, 3);
        return this.http.get<Book[]>("/api/book", {
            params: httpParams,
            context: myContext
        });
    }
    saveBook(book: Book): Observable<Book> {
        const myContext = new HttpContext().set(IS_AUTH_REQUIRED, true);
        return this.http.post<Book>("/api/book", book, {
            context: myContext
        });
    }
    deleteBook(bid: string): Observable<string> {
        const httpParams = new HttpParams().set('id', bid);
        const myContext = new HttpContext().set(IS_AUTH_REQUIRED, true);
        return this.http.delete<string>("/api/book", {
            params: httpParams,
            context: myContext
        });
    }
} 

5. Accessing HttpContext using Interceptors

Here I will create some HTTP interceptors that are accessing HttpContext.
Ex.1 : In auth interceptor, I have a context to decide if authorization headers is needed to this HTTP request or not.
auth-interceptor.ts
import { HttpRequest, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
import { IS_AUTH_REQUIRED } from '../constants';

export const authInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {
    if (req.context.get(IS_AUTH_REQUIRED) === false) {
        return next(req);
    } else {
        // code for authorisation
    }
}; 
The scenarios may be that GET request has no authorization header but POST and DELETE request must have authorization header. In this case while using HttpClient methods, we will pass IS_AUTH_REQUIRED as false for GET request and true for other requests.

Ex.2 : In caching interceptor, I will decide caching of HTTP methods using HttpContext.
caching-interceptor.ts
export const cacheInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {
    if (req.context.get(IS_CACHEABLE) === false) {
        return next(req);
    } else {
        // code for cache
    }
}; 
If IS_CACHEABLE is false, pass request handler to next interceptor without caching the request otherwise we will cache the request.
In our HTTP operations using HttpClient, I will set IS_CACHEABLE as false for those request which are not required to be cached and true for those requests which are required to be cached.

Ex.3 : In retry interceptor, I will use HttpContext to decide the retry count for HTTP methods.
retry-interceptor.ts
export const retryInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {
    const retryCount = req.context.get(RETRY_COUNT);
    return next(req).pipe(
        retry(retryCount)
    );
}; 

HttpContext is mutable :
We can update the original instance of HttpContext with new values. For instance, if we want to count the retries made by HTTP requests, we can access and set HttpContext as below.
import { HttpRequest, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http';
import { HTTP_ERROR_COUNT, RETRY_COUNT } from '../constants';
import { retry, tap } from 'rxjs';

export const authInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {
    const retryCount = req.context.get(RETRY_COUNT);
    return next(req).pipe(
        tap({
            error: () => req.context.set(HTTP_ERROR_COUNT, req.context.get(HTTP_ERROR_COUNT) + 1)
        }),
        retry(retryCount),
    );
}; 
HTTP_ERROR_COUNT will be incremented when there is request error. retry(retryCount) will retry the request max up to the configured retry count.

6. References

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us