Angular provides HTTP Interception to inspect and transform HTTP requests from your application to the server. In the tutorial, we show how to build an Angular 6 Http Log Interceptor with Node.js RestAPIs.
Related posts:
– Error Handler Angular 6 HttpClient – catchError + retry – with Node.js/Express example
Technologies
- Angular 6
- RxJS 6
- Bootstrap 4
- Visual Studio Code – version 1.24.0
- Nodejs – v8.11.3
Angular HTTP Interception
@angular/common/http
has a major feature HTTP Interception that helps inspect and transform HTTP requests.
Example Log Interceptor:
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http'; import { finalize, tap } from 'rxjs/operators'; import { LogService } from './log.service'; @Injectable() export class LoggingInterceptor implements HttpInterceptor { constructor(private log: LogService) {} intercept(req: HttpRequest, next: HttpHandler) { const started = Date.now(); let ok: string; // Log - start request this.log.add("Start request -> " + `${req.method} ${req.urlWithParams}`); return next.handle(req) .pipe( tap( // Success Path event => ok = event instanceof HttpResponse ? 'succeeded' : '', // Fail Path error => ok = 'failed' ), // Log when response observable either completes or errors finalize(() => { const elapsed = Date.now() - started; const log = `${req.method} ${req.urlWithParams} , ${ok} in ${elapsed} ms.`; // Log - end response this.log.add("End request: " + log); }) ); } } export const httpInterceptorProviders = [ { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true } ];
The intercept
method transforms a request into an Observable
.
interceptors
inspect the request on the way in and forward the request to the handle()
method of the next
object.
handle()
method transforms an HTTP request into an Observable
of HttpEvents
which include the server’s response.
What is next
object?
-> The next
object represents the next interceptor
in the chain of interceptors. The final next
in the chain is the Angular HttpClient
handler.
How to provide the interceptor?
-> Firstly, importing HTTP_INTERCEPTORS
, injection token from @angular/common/http
,
import { HTTP_INTERCEPTORS } from '@angular/common/http'; ... export const httpInterceptorProviders = [ { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true } ];
-> Then add it to the AppModule
providers array:
@NgModule({ declarations: [ ... ], imports: [ ... ], providers: [ httpInterceptorProviders ], bootstrap: [AppComponent] }) export class AppModule { }
Now you define a simple Angular Http Get request as below:
export class CustomerService { private customersUrl = 'http://localhost:8080/api/customers'; // URL to web api private handleError: HandleError; constructor( private http: HttpClient, httpErrorHandler: HttpErrorHandler, private logService: LogService ) { this.handleError = httpErrorHandler.createHandleError('CustomerService'); } getCustomers (): Observable{ const message = "Info -> Function: getCustomers. Url: " + this.customersUrl; this.logService.add(message); return this.http.get (this.customersUrl) .pipe( retry(3), catchError(this.handleError('getCustomers', [])) ); }
-> Logs with successful response:
-> Logs when Server die -> failed response:
Practice
We create 2 projects:
– Angular project:
– Node.js project:
For both projects {Angular, Node.js}, we re-use sourcecodes of the tutorials ->
Error Handler Angular 6 HttpClient – catchError + retry – with Node.js/Express example
What do we build more in the tutorial?
-> With Angular project, to do list:
- Create Log Service
- Create Log Component
- Implement Log Interceptor
- Integrate LogService with Customer Service
-> With Node.js RestAPIs, we re-use all the sourcecode in Error Handler Angular 6 HttpClient post.
Create Log Service
/src/app/log.service.ts
->
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class LogService { logs: string[] = []; add(log: string) { this.logs.push(log); } removeAll() { this.logs = []; } }
Create Log Component
– src/app/log/log.component.ts
->
import { Component, OnInit } from '@angular/core'; import { LogService } from '../log.service'; @Component({ selector: 'app-log', templateUrl: './log.component.html' }) export class LogComponent{ constructor(public logService: LogService) {} }
– src/app/log/log.component.html
->
<div *ngIf="logService.logs.length"> <h3>Logs</h3> <button type="button" class="btn btn-dark" (click)="logService.removeAll()">clear</button> <br> <ol> <li *ngFor='let log of logService.logs'> {{log}} </li> </ol> </div>
Implement Log Interceptor
– src/app/log-interceptor.ts
->
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http'; import { finalize, tap } from 'rxjs/operators'; import { LogService } from './log.service'; @Injectable() export class LoggingInterceptor implements HttpInterceptor { constructor(private log: LogService) {} intercept(req: HttpRequest, next: HttpHandler) { const started = Date.now(); let ok: string; // Log - start request this.log.add("Start request -> " + `${req.method} ${req.urlWithParams}`); return next.handle(req) .pipe( tap( // Success Path event => ok = event instanceof HttpResponse ? 'succeeded' : '', // Fail Path error => ok = 'failed' ), // Log when response observable either completes or errors finalize(() => { const elapsed = Date.now() - started; const log = `${req.method} ${req.urlWithParams} , ${ok} in ${elapsed} ms.`; // Log - end response this.log.add("End request: " + log); }) ); } } export const httpInterceptorProviders = [ { provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true } ];
– Add LogInterceptor
to providers
array of AppModule
:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { AppRoutingModule } from './app-routing/app-routing.module'; import { AppComponent } from './app.component'; import { CustomerComponent } from './customer/customer.component'; import { CustomerDetailsComponent } from './customer-details/customer-details.component'; import { AddCustomerComponent } from './add-customer/add-customer.component'; import { HttpErrorHandler } from'./http-error-handler.service'; import { httpInterceptorProviders } from './log-interceptor' import { LogComponent } from './log/log.component'; import {LogService} from './log.service' @NgModule({ declarations: [ AppComponent, CustomerComponent, CustomerDetailsComponent, AddCustomerComponent, LogComponent ], imports: [ BrowserModule, FormsModule, AppRoutingModule, HttpClientModule ], providers: [ HttpErrorHandler, LogService, httpInterceptorProviders ], bootstrap: [AppComponent] }) export class AppModule { }
Integrate LogService with Customer Service
src/app/customer.service.ts
->
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs'; import { catchError, retry } from 'rxjs/operators'; import { Customer } from './customer'; import { HttpErrorHandler, HandleError } from './http-error-handler.service'; import { LogService } from './log.service'; const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; @Injectable({ providedIn: 'root' }) export class CustomerService { private customersUrl = 'http://localhost:8080/api/customers'; // URL to web api private handleError: HandleError; constructor( private http: HttpClient, httpErrorHandler: HttpErrorHandler, private logService: LogService ) { this.handleError = httpErrorHandler.createHandleError('CustomerService'); } getCustomers (): Observable{ const message = "Info -> Function: getCustomers. Url: " + this.customersUrl; this.logService.add(message); return this.http.get (this.customersUrl) .pipe( retry(3), catchError(this.handleError('getCustomers', [])) ); } getCustomer(id: number): Observable { const url = `${this.customersUrl}/${id}`; const message = "Info -> Function: getCustomer. Url: " + url; this.logService.add(message); console.log(message); return this.http.get (url) .pipe( retry(3), catchError(this.handleError('addCustomer', null)) ); } addCustomer (customer: Customer): Observable { const message = "Info -> Function: addCustomer. Url: " + this.customersUrl; this.logService.add(message); console.log(message); return this.http.post (this.customersUrl, customer, httpOptions) .pipe( retry(3), catchError(this.handleError('addCustomer', customer)) ); } deleteCustomer (customer: Customer | number): Observable<{}> { const id = typeof customer === 'number' ? customer : customer.id; const url = `${this.customersUrl}/${id}`; const message = "Info -> Function: deleteCustomer. Url: " + url; this.logService.add(message); console.log(message); return this.http.delete(url, httpOptions) .pipe( retry(3), catchError(this.handleError('deleteCustomer', null)) ); } updateCustomer (customer: Customer): Observable { const message = "Info -> Function: updateCustomer. Url: " + this.customersUrl; this.logService.add(message); console.log(message); return this.http.put(this.customersUrl, customer, httpOptions) .pipe( catchError(this.handleError('updateCustomer', null)) ); } }
SourceCode
– Angular-6-Http-Interceptor
– Node.js-RestAPIs