**Unit Testing Code Does Not Compile** The compilation error is: ``` The compilation error is ./src/testing/http-client.spec.ts - Error: Module build failed (from ./node_modules/@ngtools/webpack/src/ivy/index.js): Error: /home/projects/obk3vc--run/src/testing/http-client.spec.ts is missing from the TypeScript compilation. Please make sure it is in your tsconfig via the 'files' or 'include' property. ``` I’m not sure what to say about unit testing HTTP in a full Standalone app. Is it different? _This is the only known remaining defect in this conversion of HTTP to Standalone._ **Edited content of `http-request-data-from-server.md`** The current version of this page is confusing. In particular * It tells readers they **should always unsubscribe** from the HttpClient method calls. This is *not true* and this example doesn't even do it. I replaced this instruction with more nuanced advice and an explanation of why it is OK to not unsubscribe to HttpClient methods. * There is a "helpful" note about using the RxJS `map` operator to transform the response. This is *not "helpfulf"*. It is *confusing* because the sample doesn't use `map` anywhere. It was unnecessary here, even if it might be helpful elsewhere. I removed this note. * The "Requesting a typed response" section seemed unclear to me, particularly because the guide begins with a `get` request that already has the `Config` return type specification. My revision attempts to make this more clear. * The bold "callout" about the `observe` and `responseType` options appears out of nowhere after "Requesting a typed response". It's disconcerting at best. I moved it to the bottom of the page and linked to it from the `options` discussion at the top. I made a few other revisions that I hope improve the readability of this page. **Corrected `http-make-jsonp-request.md`** The JSONP example, handwritten in the guide page, would not have compiled. I added one that does to `heroes.service.ts` and displayed it on this page. **Corrected `http-handle-request-errors.md` This page ended with a section called "Sending data to a server" that introduces PUT, POST, and DELETE. These features have nothing to do with error handling and the verbiage here duplicates the opening paragraphs of the next topic which does: "Send data to a server". So I deleted this section from the error handling guide page. **Archived http-setup-server-communication.md** `http-setup-server-communication.md` appears to be the original long document that has since been divided over the other pages in this folder. It shouldn’t be in the reader’s flow. I did update it for Standalone. But I also removed it from left-nav and marked as archived. PR Close #51400
6.4 KiB
HTTP - interceptor use-cases
Following are a number of common uses for interceptors.
Set default headers
Apps often use an interceptor to set default headers on outgoing requests.
The sample app has an AuthService that produces an authorization token.
Here is its AuthInterceptor that injects that service to get the token and adds an authorization header with that token to every outgoing request:
The practice of cloning a request to set new headers is so common that there's a setHeaders shortcut for it:
An interceptor that alters headers can be used for a number of different operations, including:
- Authentication/authorization
- Caching behavior; for example,
If-Modified-Since - XSRF protection
Log request and response pairs
Because interceptors can process the request and response together, they can perform tasks such as timing and logging an entire HTTP operation.
Consider the following LoggingInterceptor, which captures the time of the request,
the time of the response, and logs the outcome with the elapsed time
with the injected MessageService.
The RxJS tap operator captures whether the request succeeded or failed.
The RxJS finalize operator is called when the response observable either returns an error or completes and reports the outcome to the MessageService.
Neither tap nor finalize touch the values of the observable stream returned to the caller.
Custom JSON parsing
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 responseType is changed to 'text' to disable the built-in JSON parsing.
Then the response is parsed via the injected JsonParser.
You can then implement your own custom JsonParser.
Here is a custom JsonParser that has a special date reviver.
Finally, provide the CustomParser along with the CustomJsonInterceptor in that same httpInterceptorProviders array.
Cache requests
Interceptors can handle requests by themselves, without forwarding to next.handle().
For example, you might decide to cache certain requests and responses to improve performance. You can delegate caching to an interceptor without disturbing your existing data services.
The CachingInterceptor in the following example demonstrates this approach.
-
The
isCacheable()function determines if the request is cacheable. In this sample, only GET requests to the package search API are cacheable. -
If the request is not cacheable, the interceptor forwards the request to the next handler in the chain
-
If a cacheable request is found in the cache, the interceptor returns an
of()observable with the cached response, by-passing thenexthandler and all other interceptors downstream -
If a cacheable request is not in cache, the code calls
sendRequest(). This function forwards the request tonext.handle()which ultimately calls the server and returns the server's response.
Notice how sendRequest() intercepts the response on its way back to the application.
This method pipes the response through the tap() operator, whose callback adds the response to the cache.
The original response continues untouched back up through the chain of interceptors to the application caller.
Data services, such as PackageSearchService, are unaware that some of their HttpClient requests actually return cached responses.
Use interceptors to request multiple values
The HttpClient.get() method normally returns an observable that emits a single value, either the data or an error.
An interceptor can change this to an observable that emits multiple values.
The following revised version of the CachingInterceptor optionally returns an observable that immediately emits the cached response, sends the request on to the package search API, and emits again later with the updated search results.
The cache-then-refresh option is triggered by the presence of a custom x-refresh header.
A checkbox on the PackageSearchComponent toggles a withRefresh flag, which is one of the arguments to PackageSearchService.search().
That search() method creates the custom x-refresh header and adds it to the request before calling HttpClient.get().
The revised CachingInterceptor sets up a server request whether there's a cached value or not, using the same sendRequest() method described above.
The results$ observable makes the request when subscribed.
- If there's no cached value, the interceptor returns
results$. - If there is a cached value, the code pipes the cached response onto
results$. This produces a recomposed observable that emits two responses, so subscribers will see a sequence of these two responses: - The cached response that's emitted immediately
- The response from the server, that's emitted later
@reviewed 2022-11-08