Utilizing HttpContext in Angular for Flexible Interceptors
Written on
Dynamic Interceptors in Angular
In Angular, interceptors are essential for implementing centralized functionality for HTTP requests. They can handle tasks such as adding authorization tokens to API calls, retrying requests upon failure, returning cached responses, or redirecting users to an error page during HTTP errors. However, there are scenarios where we might want to introduce exceptions to this behavior.
Understanding Exceptions in Interceptors
Interceptors function similarly to fixed functions without parameters. For instance:
// Fixed function
function double() {
return 2 * 2;
}
This function is static and consistently returns the same output. In contrast, a more flexible version allows for dynamic input:
// Generic function
function double(x) {
return x * x;
}
Just like the flexible function, we can enhance the rigidity of interceptors. By default, any logic written within an interceptor executes for every API request. However, there may be instances where you want to:
- Exclude specific API calls from caching.
- Prevent navigation to the error page for certain failed requests.
- Omit the addition of an auth token for particular HTTP requests.
Is it possible to make interceptors more dynamic, akin to functions that accept parameters?
Introducing HTTP Context
With the release of Angular version 12, HttpContext was introduced, allowing developers to pass metadata to HTTP interceptors. This feature enables users to send arbitrary data during an HTTP call via the HTTPClient.
The HttpContext operates similarly to a map, using a map internally to store values:
export class HttpContext {
private readonly map = new Map<HttpContextToken<unknown>, unknown>();
// All the map functions
set<T>(token: HttpContextToken<T>, value: T): HttpContext
get<T>(token: HttpContextToken<T>): T
delete(token: HttpContextToken<unknown>): HttpContext
has(token: HttpContextToken<unknown>): boolean
keys(): IterableIterator<HttpContextToken<unknown>>
}
The key to this map is based on a simple class known as an HTTP context token.
Using Class Tokens for Enhanced Safety
Employing a class as a key rather than a simple string offers two significant advantages:
- It allows the pre-definition of context tokens, thus limiting the context to specific tokens rather than arbitrary keys.
- It promotes type safety by specifying the value type while creating the token.
Example: Implementing HTTP Context
Let’s consider an example involving an interceptor that redirects users to an error page for HTTP failures:
@Injectable()
export class ErrorResponseInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
return next.handle(req).pipe(
catchError((err: unknown) => {
this.router.navigateToUrl('/server-error');
return EMPTY;
})
);
}
}
In cases where we don't want to redirect the user during specific API failures, we can utilize HTTP context to communicate that intent.
Creating an HTTP Context
First, we need to establish a context token:
export const BYPASS_ERROR_PAGE = new HttpContextToken<boolean>(() => false);
Here, we define a boolean type with a default value of false. Next, we create our context:
const context = new HttpContext();
context.set(BYPASS_ERROR_PAGE, true);
Finally, we can pass this context during the API call:
getStats(): Observable<StatsResponse> {
return this.http.get<StatsR>(BASE_API/${stats}, { context });
}
Using HTTP Context in Interceptors
We can retrieve the context from the API request by utilizing the context token:
const context = request.context.get(BYPASS_ERROR_PAGE);
This allows us to avoid redirecting the user during an API failure:
@Injectable()
export class ErrorResponseInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const errorBypass = request.context.get(BYPASS_ERROR_PAGE) === true;
return next.handle(req).pipe(
catchError((err: unknown) => {
if (!errorBypass) {
this.router.navigateToUrl('/server-error');}
return EMPTY;
})
);
}
}
Conclusion
In many applications, handling HTTP requests under varying conditions is crucial. HttpContext empowers developers to create dynamic interceptors with type safety. By utilizing context tokens, we can pass arbitrary data to interceptors, enhancing their flexibility.
Discover how to effectively use Http Interceptor in Angular 17 by watching this video.
Learn to build your first Angular HTTP Interceptor with this tutorial.