Skip to content

Commit

Permalink
docs: add metadata documentation to httpClient (#41706)
Browse files Browse the repository at this point in the history
PR Close #41706
  • Loading branch information
kapunahelewong authored and thePunderWoman committed Apr 26, 2021
1 parent e47ea29 commit 09abee3
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 1 deletion.
@@ -0,0 +1,50 @@
// #docplaster
import {HttpClient, HttpContext, HttpContextToken, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs';
/*
// #docregion reading-context
import {retry} from 'rxjs';
// #enddocregion reading-context
*/
// #docregion mutable-context
import {retry, tap} from 'rxjs/operators';
// #enddocregion mutable-context

// #docregion context-token, mutable-context
export const RETRY_COUNT = new HttpContextToken(() => 3);
// #enddocregion context-token
export const ERROR_COUNT = new HttpContextToken(() => 0);
// #enddocregion mutable-context

export class FakeService {
constructor(private httpClient: HttpClient) {
// #docregion set-context
this.httpClient
.get('/data/feed', {
context: new HttpContext().set(RETRY_COUNT, 5),
})
.subscribe(results => {/* ... */});
// #enddocregion set-context
}
}

// #docregion reading-context, mutable-context

export class RetryInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const retryCount = req.context.get(RETRY_COUNT);

return next.handle(req).pipe(
// #enddocregion reading-context
tap(null,
() => {
// An error has occurred, so increment this request's ERROR_COUNT.
req.set(ERROR_COUNT, req.get(ERROR_COUNT) + 1);
}),
// #docregion reading-context
// Retry the request a configurable number of times.
retry(retryCount),
);
}
}
// #enddocregion reading-context, mutable-context
49 changes: 48 additions & 1 deletion aio/content/guide/http.md
Expand Up @@ -812,7 +812,7 @@ Neither `tap` nor `finalize` touch the values of the observable stream returned
Interceptors can be used to replace the built-in JSON parsing with a custom implementation.

The `CustomJsonInterceptor` in the following example demonstrates how to achieve this.
If the intercepted request expects a `'json'` response, the `reponseType` is changed to `'text'`
If the intercepted request expects a `'json'` response, the `responseType` is changed to `'text'`
to disable the built-in JSON parsing. Then the response is parsed via the injected `JsonParser`.

<code-example
Expand Down Expand Up @@ -1197,3 +1197,50 @@ Alternatively, you can call `request.error()` with an `ErrorEvent`.
path="http/src/testing/http-client.spec.ts"
region="network-error">
</code-example>


## Passing metadata to interceptors

Many interceptors require or benefit from configuration. Consider an interceptor that retries failed requests.
By default, the interceptor might retry a request three times, but you might want to override this retry count for particularly error-prone or sensitive requests.

`HttpClient` requests contain a _context_ that can carry metadata about the request.
This context is available for interceptors to read or modify, though it is not transmitted to the backend server when the request is sent.
This allows applications or other interceptors to tag requests with configuration parameters, such as how many times to retry a request.

### Creating a context token

Angular stores and retrieves a value in the context using an `HttpContextToken`.
You can create a context token using the `new` operator, as in the following example:

<code-example path="http/src/app/http-interceptors/retry-interceptor.ts" region="context-token" header="creating a context token"></code-example>

The lambda function `() => 3` passed during the creation of the `HttpContextToken` serves two purposes:

1. It allows TypeScript to infer the type of this token: `HttpContextToken<number>`.
The request context is type-safe&mdash;reading a token from a request's context returns a value of the appropriate type.

1. It sets the default value for the token.
This is the value that the request context returns if no other value has been set for this token.
Using a default value avoids the need to check if a particular value is set.

### Setting context values when making a request

When making a request, you can provide an `HttpContext` instance, in which you have already set the context values.

<code-example path="http/src/app/http-interceptors/retry-interceptor.ts" region="set-context" header="setting context values"></code-example>

### Reading context values in an interceptor

Within an interceptor, you can read the value of a token in a given request's context with `HttpContext.get()`.
If you have not explicitly set a value for the token, Angular returns the default value specified in the token.

<code-example path="http/src/app/http-interceptors/retry-interceptor.ts" region="reading-context" header="reading context values in an interceptor"></code-example>

### Contexts are mutable

Unlike most other aspects of `HttpRequest` instances, the request context is mutable and persists across other immutable transformations of the request.
This allows interceptors to coordinate operations through the context.
For instance, the `RetryInterceptor` example could use a second context token to track how many errors occur during the execution of a given request:

<code-example path="http/src/app/http-interceptors/retry-interceptor.ts" region="mutable-context" header="coordinating operations through the context"></code-example>

0 comments on commit 09abee3

Please sign in to comment.