Angular HttpContext Example
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
.
Contents
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)
HttpContextToken
for boolean.
const MY_CONTEXT_TOKEN = new HttpContextToken<boolean>(() => false);
const MY_CONTEXT_TOKEN = new HttpContextToken<string>(() => "");
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 ofHttpContext
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 setHttpContext
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);
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 accessingHttpContext
.
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 } };
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 } };
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), ); };
retry(retryCount)
will retry the request max up to the configured retry count.