Introduction
HttpInterceptors are use to handle and intercpts the HttpRequest and HttpResponse.
In this article we will try to use the HttpInterceptors for the following reasons,
- Creating an Error Interceptor.
- Creating JSON Web Tokens (JWT)interceptor.
- Creating a Fake-Backend Service with Interceptor.
Let’s create an application(called as Event Management) which will demonstrate the use of these Interceptors.
Application will have the following functionalities,
- Register of user
- Login
- Authentication of user
- Events list
- Paid (Members) event list.
- AuthGuard for routing.
Let’s create an application.
ng new AuthApp
Create below components with Angular CLI command,
- Register ( ng g c register )
- Login
- Logout
- Events
- Paid-events
Create folders
- _guards ( to keep the auth guard file )
- _helpers ( To keep the Interceptors )
- _model ( to keep the models )
- _services ( To keep the services )
- Create class user inside _model directory.
- export class User {
- id: number;
- username: string;
- password: string;
- firstName: string;
- lastName: string;
- isAdmin: boolean;
- token: string;
- }
Add auth.service.ts through command inside _services directory,
- import { Injectable } from '@angular/core';
- import { HttpClient } from '@angular/common/http';
- import { map } from 'rxjs/operators';
- import { Observable, BehaviorSubject } from 'rxjs';
- import { User } from '../_model/user';
-
- @Injectable({
- providedIn: 'root'
- })
- export class AuthService {
-
- private currentUserSubject: BehaviorSubject<User>;
- public currentUser: Observable<User>;
- private _loginUrl = "api/login";
- private _registerUrl = "api/register";
- constructor(private http: HttpClient) {
- this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(sessionStorage.getItem('currentUser')));
- this.currentUser = this.currentUserSubject.asObservable();
- }
-
- public get currentUserValue(): User {
- return this.currentUserSubject.value;
- }
-
- getAuthToken(){
- return "TOKEN-001001010100111";
- }
- registerUser(user) {
- return this.http.post<any>(this._registerUrl, user);
- }
- login(user) {
- return this.http.post<any>(this._loginUrl, user);
- }
-
- authenticate(loginData) {
- return this.http.post<any>("http://localhost:4200/api/login", loginData)
- .pipe(map(user => {
-
- if (user && user.token) {
-
- sessionStorage.setItem('currentUser', JSON.stringify(user));
- this.currentUserSubject.next(user);
- }
-
- return user;
- }));
- }
-
- logout() {
-
- sessionStorage.removeItem('currentUser');
- this.currentUserSubject.next(null);
- }
- }
Create event.service.ts through command inside _services directory,
- import { Injectable } from '@angular/core';
- import { HttpClient } from '@angular/common/http';
-
- @Injectable({
- providedIn: 'root'
- })
- export class EventService {
-
- private _eventsUrl = "api/events";
- private _paidEventsUrl = "api/paidEvents";
-
- constructor(private http: HttpClient) { }
-
- getEvents() {
- return this.http.get<any>(this._eventsUrl);
- }
-
- getPaidEvents() {
- return this.http.get<any>(this._paidEventsUrl);
- }
- }
Create file auth.guard.ts in _guard directory and add the below contents,
- import { Injectable } from '@angular/core';
- import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
- import { AuthService } from '../_services/auth.service';
-
- @Injectable({ providedIn: 'root' })
- export class AuthGuard implements CanActivate {
- constructor(
- private router: Router,
- private authenticationService: AuthService
- ) {}
-
- canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
- console.log("CanActivate method called from class AuthGuard..");
-
- const currentUser = this.authenticationService.currentUserValue;
- if (currentUser) {
-
- this.router.navigate(['/paidevents'], { queryParams: { returnUrl: state.url }});
-
- return true;
- }
-
-
- this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
- return false;
- }
- }
Create error.interceptors.ts in _helpers directory,
- import { Injectable } from '@angular/core';
- import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
- import { Observable, throwError } from 'rxjs';
- import { catchError } from 'rxjs/operators';
- import { AuthService } from '../_services/auth.service';
- import { Router } from '@angular/router';
-
- @Injectable()
- export class ErrorInterceptor implements HttpInterceptor {
- constructor(private authenticationService: AuthService, private router: Router) { }
-
- intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
-
- return next.handle(request).pipe(catchError(err => {
- if (err.status === 401) {
- this.authenticationService.logout();
- this.router.navigate(['/login']);
- }
-
- const error = err.error.message || err.statusText;
- return throwError(error);
- }))
- }
- }
Create jwt.interceptor.ts in _helpers directory.
- import { Injectable } from '@angular/core';
- import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
- import { Observable } from 'rxjs';
- import { AuthService } from '../_services/auth.service';
-
- @Injectable()
- export class JwtInterceptor implements HttpInterceptor {
- constructor(private authenticationService: AuthService) { }
-
- intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
- console.log("Intercept method called from JWTInterceptor..");
-
- let currentUser = this.authenticationService.currentUserValue;
- if (currentUser && currentUser.token) {
- request = request.clone({
- setHeaders: {
- Authorization: `Bearer ${currentUser.token}`,
- admin: "Irshad",
- Host: "localhost://3400"
- }
- });
- }
- return next.handle(request);
- }
- }
Create fake-backend-intercepter.provider.ts in _helpers directory,
- import { Injectable } from '@angular/core';
- import { HttpRequest, HttpResponse, HttpHandler, HttpEvent, HttpInterceptor, HTTP_INTERCEPTORS } from '@angular/common/http';
- import { Observable, of, throwError } from 'rxjs';
- import { delay, mergeMap, materialize, dematerialize } from 'rxjs/operators';
- import * as jwt_decode from 'jwt-decode';
-
-
- let users = JSON.parse(localStorage.getItem('users')) || [];
-
- @Injectable()
- export class FakeBackendInterceptor implements HttpInterceptor {
- constructor(){}
-
-
- intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
-
- const adminToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJ1c2VybmFtZSI6ImFkbWluIiwiZmlyc3ROYW1lIjoiTW9oYW1tYWQiLCJsYXN0TmFtZSI6Iklyc2hhZCJ9.apXr_qMJrzvYczZbfW23UfJdzmMaaCA8d7Njm8tN9wI";
-
- const userToken = "fake-jwt-token";
- const { url, method, headers, body } = request;
- return of(null)
- .pipe(mergeMap(handleRoute))
- .pipe(materialize())
- .pipe(delay(500))
- .pipe(dematerialize());
-
- function handleRoute() {
- switch (true) {
- case url.endsWith('/api/login') && method === 'POST':
- return authenticate();
- case url.endsWith('api/register') && method === 'POST':
- return register();
- case url.endsWith('/users') && method === 'GET':
- return getUsers();
- case url.endsWith('/events') && method === 'GET':
- return getEvents();
- case url.endsWith('/paidEvents') && method === 'GET':
- return getPaidEvents();
- case url.match(/\/users\/\d+$/) && method === 'DELETE':
- return deleteUser();
- default:
- return next.handle(request);
- }
- }
-
- function authenticate() {
- const { username, password } = body;
- if(username=='admin' && password=='1'){
- var admin = jwt_decode(adminToken);
- admin.isAdmin = true;
- admin.token = adminToken;
- return ok(admin);
- }
- const user = users.find(x => x.username === username && x.password === password);
- if (!user) return error('Username or password is incorrect');
-
- return ok({
- id: user.id,
- username: user.username,
- firstName: user.firstName,
- lastName: user.lastName,
- isAdmin: false,
- token: userToken
- })
- }
-
- function register() {
- const user = body
-
- if (users.find(x => x.username === user.username)) {
- return error('Username "' + user.username + '" is already taken');
- }
-
- user.id = users.length ? Math.max(...users.map(x => x.id)) + 1 : 1;
- user.isAdmin = false;
- users.push(user);
- localStorage.setItem('users', JSON.stringify(users));
-
- return ok('Successfully created');
- }
-
- function getUsers() {
- if (!isLoggedIn()) return unauthorized();
- return ok(users);
- }
-
- function deleteUser() {
- if (!isLoggedIn()) return unauthorized();
-
- users = users.filter(x => x.id !== idFromUrl());
- localStorage.setItem('users', JSON.stringify(users));
- return ok();
- }
-
- function ok(body?) {
- return of(new HttpResponse({ status: 200, body }))
- }
-
- function error(message) {
- return throwError({ error: { message } });
- }
-
- function unauthorized() {
- return throwError({ status: 401, error: { message: 'Unauthorised' } });
- }
-
- function isLoggedIn() {
- return headers.get('Authorization') === 'Bearer ' + userToken || headers.get('Authorization') === 'Bearer '+ adminToken;
- }
-
- function getEvents() {
- return ok(events);
- }
-
- function getPaidEvents() {
- if (!isLoggedIn()) return unauthorized();
- return ok(paidEvents);
- }
-
- function idFromUrl() {
- const urlParts = url.split('/');
- return parseInt(urlParts[urlParts.length - 1]);
- }
- }
- }
-
- let events = [
- {
- "id": "1",
- "name": "event1",
- "place": "place1",
- "date": "date1"
- },
- {
- "id": "2",
- "name": "event3",
- "place": "place2",
- "date": "date2"
- },
- {
- "id": "3",
- "name": "event3",
- "place": "place3",
- "date": "date3"
- },
- {
- "id": "4",
- "name": "event4",
- "place": "place4",
- "date": "date4"
- },
- {
- "id": "5",
- "name": "event5",
- "place": "place5",
- "date": "date5"
- }
- ];
-
- let paidEvents = [
- {
- "id": "1",
- "name": "paidEvent1",
- "place": "place1",
- "date": "date1"
- },
- {
- "id": "2",
- "name": "paidEvent3",
- "place": "place2",
- "date": "date2"
- },
- {
- "id": "3",
- "name": "paidEvent3",
- "place": "place3",
- "date": "date3"
- },
- {
- "id": "4",
- "name": "paidEvent4",
- "place": "place4",
- "date": "date4"
- },
- {
- "id": "5",
- "name": "paidEvent5",
- "place": "place5",
- "date": "date5"
- }
- ];
-
- export const fakeBackendProvider = {
-
- provide: HTTP_INTERCEPTORS,
- useClass: FakeBackendInterceptor,
- multi: true
- };
Create app-routing.module.ts
- import { NgModule } from '@angular/core';
- import { Routes, RouterModule } from '@angular/router';
- import { EventsComponent } from './events/events.component';
- import { PaidEventsComponent } from './paid-events/paid-events.component';
- import { LoginComponent } from './login/login.component';
- import { RegisterComponent } from './register/register.component';
- import { AuthGuard } from './_guards/auth.guard';
-
- const routes: Routes = [
- {
- path: '',
- component: EventsComponent,
- canActivate: [AuthGuard]
- },
- {
- path: 'events',
- component: EventsComponent,
- },
- {
- path: 'paidevents',
- component: PaidEventsComponent
- },
- {
- path: 'login',
- component: LoginComponent
- },
- {
- path: 'register',
- component: RegisterComponent
- }
- ];
-
- @NgModule({
- imports: [RouterModule.forRoot(routes)],
- exports: [RouterModule]
- })
- export class AppRoutingModule { }
Let’s start with component html and ts files.
Open login.component.ts and add the below contents,
- import { Component, OnInit } from '@angular/core';
- import { AuthService } from '../_services/auth.service';
- import { Router } from '@angular/router';
-
- @Component({
- selector: 'app-login',
- templateUrl: './login.component.html',
- styleUrls: ['./login.component.css']
- })
- export class LoginComponent implements OnInit {
-
- loginData = { username: '', password: ''};
- constructor(private _authservice: AuthService, private router: Router) {
-
- if (this._authservice.currentUserValue) {
- this.router.navigate(['/paidevents']);
- }
- }
-
- ngOnInit(): void {
- }
-
- login() {
- console.log(this.loginData);
- this._authservice.authenticate(this.loginData).subscribe(
- data => {
- console.log("loggedin : ", data );
- this.router.navigate(['/paidevents'], { queryParams: { }});
- },
- error => {
- console.log("erorr: ", error);
- }
- );
- console.log('Logged in successfully!');
- }
- }
Open login.component.html and add the below contents,
- <div class="row pt-5">
- <div class="co-md-6 mx-auto">
- <div class="card rounded-0">
- <div class="card-header">
- <h3 class="mb-0">Login</h3>
- </div>
- <div class="card-body">
- <form class="form">
- <div class="form-group">
- <label for="">Username</label>
- <input [(ngModel)]="loginData.username" name="username" type="text" class="form-control rounded-0" required>
- </div>
- <div class="form-group">
- <label for="">Password</label>
- <input [(ngModel)]="loginData.password" name="password" type="text" class="form-control rounded-0" required>
- </div>
- <a routerLink="/register" class="btn btn-primary float-left">Register</a>
- <button (click)="login()" type="button" class="btn btn-primary float-right">Login</button>
- </form>
- </div>
- </div>
- </div>
- </div>
Open register.component.ts and add the below contents,
- import { Component, OnInit } from '@angular/core';
- import { AuthService } from '../_services/auth.service';
- import { User } from '../_model/user';
- import { Router } from '@angular/router';
- import { Subscription } from 'rxjs';
-
- @Component({
- selector: 'app-register',
- host: { '[@slideInOutAnimation]': '' },
- templateUrl: './register.component.html',
- styleUrls: ['./register.component.css']
- })
- export class RegisterComponent implements OnInit {
-
- component: any;
- subscription: Subscription;
- registerResult: number = 0;
- registerUserData = new User();
- constructor(private _authservice: AuthService, private router: Router) {
-
- if (this._authservice.currentUserValue) {
- this.router.navigate(['/paidevents']);
- }
- }
-
- ngOnInit(): void {
- this.registerUserData.username
- = this.registerUserData.firstName
- = this.registerUserData.lastName
- = this.registerUserData.password
- = ""
- }
-
- Register(user){
- console.log(user);
- this._authservice.registerUser(user).subscribe(data=>
- {
- console.log("login Details:", data);
- this.router.navigate(['/login'], { queryParams: { }});
- },
- error => {
- console.log("Error: ", error);
- },
- ()=>{
- console.log("login process complete!");
- });
- console.log('Logged in!');
- }
- }
Open register.component.html and add the below contents,
- <div class="row pt-5">
- <div class="co-md-6 mx-auto">
- <div class="card rounded-0">
- <div class="card-header">
- <h3 class="mb-0">Register</h3>
- </div>
- <div class="card-body">
- <form class="form" #newUserForm="ngForm" (ngSubmit)="Register(newUserForm.value)">
- <div class="form-group">
- <label for="">Username</label>
- <input [(ngModel)]="registerUserData.username" name="username" type="text" class="form-control rounded-0" required>
- </div>
- <div class="form-group">
- <label for="">First Name</label>
- <input [(ngModel)]="registerUserData.firstName" name="firstName" type="text" class="form-control rounded-0" required>
- </div>
- <div class="form-group">
- <label for="">Last Name</label>
- <input [(ngModel)]="registerUserData.lastName" name="lastName" type="text" class="form-control rounded-0" required>
- </div>
- <div class="form-group">
- <label for="">Password</label>
- <input [(ngModel)]="registerUserData.password" name="password" type="password" class="form-control rounded-0" required>
- </div>
- <a routerLink="/login" class="btn btn-primary float-left">Login</a>
- <button type="submit" class="btn btn-primary float-right">Register</button>
- </form>
- </div>
- </div>
- </div>
- </div>
Open events.component.ts and add the below contents,
- import { Component, OnInit } from '@angular/core';
- import { EventService } from '../_services/event.service';
-
- @Component({
- selector: 'app-events',
- templateUrl: './events.component.html',
- styleUrls: ['./events.component.css']
- })
- export class EventsComponent implements OnInit {
-
- public events: any[];
- constructor(private _eventService: EventService) { }
-
- getAllEvents(){
- this._eventService.getEvents().subscribe(
- data => this.events = data,
- error => console.log('Error in getting event list ', error),
- () => {console.log('success to get event list...');
- });
- }
- ngOnInit(): void {
- this.getAllEvents();
- }
- }
Open events.component.html and add the below contents,
- <h2>Events Details:</h2>
- <table class="table">
- <thead>
- <tr><th>Id</th><th>Name</th><th>Place</th><th>Date</th></tr>
- </thead>
- <tbody>
- <tr *ngFor="let event of events, index as i">
- <td>{{event.id}}</td>
- <td>{{event.name}}</td>
- <td>{{event.place}}</td>
- <td>{{event.date}}</td>
- </tr>
- </tbody>
- </table>
Open paid-events.component.ts and add the below contents,
- import { Component, OnInit } from '@angular/core';
- import { AuthService } from '../_services/auth.service';
- import { EventService } from '../_services/event.service';
-
- @Component({
- selector: 'app-paid-events',
- templateUrl: './paid-events.component.html',
- styleUrls: ['./paid-events.component.css']
- })
- export class PaidEventsComponent implements OnInit {
-
- public events: any[];
- constructor(private _eventService: EventService) { }
-
- getAllEvents(){
- this._eventService.getPaidEvents().subscribe(
- data => this.events = data,
- error => console.log('Error in getting event list'),
- () => {console.log('success to get event list...');
- });
- }
-
- ngOnInit(): void {
- this.getAllEvents();
- }
- }
Open paid-events.component.html and add the below contents,
- <header></header>
- <hr>
- <table class="table">
- <thead>
- <tr><th>Id</th><th>Name</th><th>Place</th><th>Date</th></tr>
- </thead>
- <tbody>
- <tr *ngFor="let event of events, index as i">
- <td>{{event.id}}</td>
- <td>{{event.name}}</td>
- <td>{{event.place}}</td>
- <td>{{event.date}}</td>
- </tr>
- </tbody>
- </table>
Open app.module.ts and be sure that it contains the below contents,
- import { BrowserModule } from '@angular/platform-browser';
- import { NgModule } from '@angular/core';
- import { FormsModule } from '@angular/forms';
- import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
- import { AppRoutingModule } from './app-routing.module';
- import { AppComponent } from './app.component';
- import { RegisterComponent } from './register/register.component';
- import { LoginComponent } from './login/login.component';
- import { EventsComponent } from './events/events.component';
- import { PaidEventsComponent } from './paid-events/paid-events.component';
- import { fakeBackendProvider } from './_helpers/fake-backend-intercepter.provider';
- import { JwtInterceptor } from './_helpers/jwt.interceptor';
- import { ErrorInterceptor } from './_helpers/error.interceptor';
- import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
- import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
-
- @NgModule({
- declarations: [
- AppComponent,
- RegisterComponent,
- LoginComponent,
- EventsComponent,
- PaidEventsComponent
- ],
- imports: [
- BrowserModule,
- FormsModule,
- HttpClientModule,
- AppRoutingModule,
- BrowserAnimationsModule,
- NgbModule
- ],
- providers: [
- { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
- { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
- fakeBackendProvider
- ],
- bootstrap: [AppComponent]
- })
- export class AppModule { }
Open app.component.ts and add the below contents,
- import { Component } from '@angular/core';
- import { AuthService } from './_services/auth.service';
- import { Router } from '@angular/router';
-
- @Component({
- selector: 'app-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.css']
- })
- export class AppComponent {
- constructor(private _authservice: AuthService, private router: Router){
- console.log(_authservice);
- }
-
- isLoggedIn(){
- return this._authservice.currentUserValue;
- }
-
- logout()
- {
- this._authservice.logout();
- this.router.navigate(['/login']);
- }
-
- title = 'AuthApp';
- }
Open app.component.html and add the below contents,
- <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
- <a class="navbar-brand" href="#">EMS</a>
- <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
- <span class="navbar-toggler-icon"></span>
- </button>
-
- <div class="collapse navbar-collapse" id="navbarSupportedContent">
- <ul class="navbar-nav mr-auto">
- <li class="nav-item active">
- <a class="nav-link" routerLink="/events" routerLinkActive="active">Events<span class="sr-only">(current)</span></a>
- </li>
- <li class="nav-item" *ngIf="isLoggedIn()">
- <a class="nav-link" routerLink="/paidevents" routerLinkActive="active">Members</a>
- </li>
- <li class="nav-item" *ngIf="!isLoggedIn()">
- <a class="nav-link" routerLink="/login" routerLinkActive="active">Login</a>
- </li>
- <li class="nav-item" *ngIf="!isLoggedIn()">
- <a class="nav-link" routerLink="/register" routerLinkActive="active">Register</a>
- </li>
- <li class="nav-item" *ngIf="isLoggedIn()" (click)="logout()">
- <a class="nav-link" routerLink="/register" routerLinkActive="active">Logout</a>
- </li>
- </ul>
- </div>
- </nav>
- <div class="container">
- <router-outlet></router-outlet>
- </div>
Your directory structure will be as below,
Run the application
Login with the register user (By default one user is already there user: ‘admin’ password : ‘1’)
Successful logged in user will be able to see the page Members (paid-event), otherwise Events page will be visible to all.
Click on Events menu
Click on Logout and you will be redirect to Login page.
Click on Register to register user,
We can see the Member menu only appears to the logged in user with the help of AuthGuard. You can check canActivate in app-routing.module.ts.
While working you can check the fake-backend-service provider to check for the HttpRequest for handling routes, Authenticate user, GetEvents/GetPaidEvents and other methods.
Interceptor jwt.interceptor is used to check the HttpRequest and will authenticate the user and handle the request with the logged in user with proper data and token information.
Error Interceptor will handle the request, catch the error and will be used to redirect and other operations that need to be performed.
Thank you.