A long-requested feature for HttpClient is the ability to store and retrieve custom metadata for requests, especially in interceptors. This commit implements this functionality via a new context object for requests. Each outgoing HttpRequest now has an associated "context", an instance of the HttpContext class. An HttpContext can be provided when making a request, or if not then an empty context is created for the new request. This context shares its lifecycle with the entire request, even across operations that change the identity of the HttpRequest instance such as RxJS retries. The HttpContext functions as an expando. Users can create typed tokens as instances of HttpContextToken, and read/write a value for the key from any HttpContext object. This commit implements the HttpContext functionality. A followup commit will add angular.io documentation. PR Close #25751
99 lines
2.6 KiB
TypeScript
99 lines
2.6 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
/**
|
|
* A token used to manipulate and access values stored in `HttpContext`.
|
|
*
|
|
* @publicApi
|
|
*/
|
|
export class HttpContextToken<T> {
|
|
constructor(public readonly defaultValue: () => T) {}
|
|
}
|
|
|
|
/**
|
|
* Http context stores arbitrary user defined values and ensures type safety without
|
|
* actually knowing the types. It is backed by a `Map` and guarantees that keys do not clash.
|
|
*
|
|
* This context is mutable and is shared between cloned requests unless explicitly specified.
|
|
*
|
|
* @usageNotes
|
|
*
|
|
* ### Usage Example
|
|
*
|
|
* ```typescript
|
|
* // inside cache.interceptors.ts
|
|
* export const IS_CACHE_ENABLED = new HttpContextToken<boolean>(() => false);
|
|
*
|
|
* export class CacheInterceptor implements HttpInterceptor {
|
|
*
|
|
* intercept(req: HttpRequest<any>, delegate: HttpHandler): Observable<HttpEvent<any>> {
|
|
* if (req.context.get(IS_CACHE_ENABLED) === true) {
|
|
* return ...;
|
|
* }
|
|
* return delegate.handle(req);
|
|
* }
|
|
* }
|
|
*
|
|
* // inside a service
|
|
*
|
|
* this.httpClient.get('/api/weather', {
|
|
* context: new HttpContext().set(IS_CACHE_ENABLED, true)
|
|
* }).subscribe(...);
|
|
* ```
|
|
*
|
|
* @publicApi
|
|
*/
|
|
export class HttpContext {
|
|
private readonly map = new Map<HttpContextToken<unknown>, unknown>();
|
|
|
|
/**
|
|
* Store a value in the context. If a value is already present it will be overwritten.
|
|
*
|
|
* @param token The reference to an instance of `HttpContextToken`.
|
|
* @param value The value to store.
|
|
*
|
|
* @returns A reference to itself for easy chaining.
|
|
*/
|
|
set<T>(token: HttpContextToken<T>, value: T): HttpContext {
|
|
this.map.set(token, value);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the value associated with the given token.
|
|
*
|
|
* @param token The reference to an instance of `HttpContextToken`.
|
|
*
|
|
* @returns The stored value or default if one is defined.
|
|
*/
|
|
get<T>(token: HttpContextToken<T>): T {
|
|
if (!this.map.has(token)) {
|
|
this.map.set(token, token.defaultValue());
|
|
}
|
|
return this.map.get(token) as T;
|
|
}
|
|
|
|
/**
|
|
* Delete the value associated with the given token.
|
|
*
|
|
* @param token The reference to an instance of `HttpContextToken`.
|
|
*
|
|
* @returns A reference to itself for easy chaining.
|
|
*/
|
|
delete(token: HttpContextToken<unknown>): HttpContext {
|
|
this.map.delete(token);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @returns a list of tokens currently stored in the context.
|
|
*/
|
|
keys(): IterableIterator<HttpContextToken<unknown>> {
|
|
return this.map.keys();
|
|
}
|
|
} |