masaj salonu masaj salonları
Home » Advertising » New HTTP Interface With Interceptors

New HTTP Interface With Interceptors

In this blog post, I want to explore the latest HTTP interface from Angular which was introduced in Angular 4.3.

We all need to get our data from a source, mostly this is done via HTTP and any REST backend (like Node or ASP.NET Core, etc.) or even from files in the *.json format. However, this has been possible for a while and is one of the key features of an SPA, but Angular introduced an improved version of the HTTP API in version 4.3. Let’s take a look at this one.

Imports // Module

The new module is provided via the HttpClientModule interface. So you have to import it first in your module.

Change:

import { HttpModule } from '@angular/http';

to:

import { HttpClientModule } from '@angular/common/http';

// ...
import { HttpClientModule } from '@angular/common/http';
// ...

@NgModule({
    imports: [
        // ...
        HttpClientModule,
        // ...
    ],

    declarations: [ ... ],

    providers: [ ... ],

    exports: [ ... ]
})

export class HomeModule { }

Dependency Injection // Constructors

Now you will notice that you do not have a “provider for HTTP” anymore. So we have to change that as well:

So everywhere you inject:

constructor(private http: Http) { }

You have to change that to:

import { HttpClient } from '@angular/common/http';
// ...
constructor(private http: HttpClient) { }

Methods // HTTP Verbs

We can use the HTTP methods around GET, POST, etc., in a new way. The .json() is not necessary anymore. JSON is now assumed as a standard. So that means we can get rid of the first .map((response: Response) = MyTyperesponse.json()) method. In addition to that, the methods are now generic, which means that they can take the type directly and you do not need to cast it anymore. So from:

public get(): ObservableMyType = {
    return this.http.get(url)
        .map((response: Response) = MyTyperesponse.json());
}

to:

getT(url: string): ObservableT {
    return this.http.getT(url);
}

or, for a post request, its pretty much straight forward as well:

postT(url: string, body: string): ObservableT {
    return this.http.postT(url, body);
}

Headers

To apply headers, we can simply add another parameter to the get function passing an object with a headers property.

So, this: 

getT(url: string): ObservableT {
    return this.http.getT(url);
}

postT(url: string, body: string): ObservableT {
    return this.http.postT(url, body);
}

becomes:

getT(url: string, headers?: HttpHeaders | null): ObservableT {
    const expandedHeaders = this.prepareHeader(headers);
    return this.http.getT(url, expandedHeaders);
}

postT(url: string, body: string, headers?: HttpHeaders | null): ObservableT {
    const expandedHeaders = this.prepareHeader(headers);
    return this.http.postT(url, body, expandedHeaders);
}

//...

private prepareHeader(headers: HttpHeaders | null): object {
    headers = headers || new HttpHeaders();

    headers = headers.set('Content-Type', 'application/json');
    headers = headers.set('Accept', 'application/json');

    return {
        headers
    }
}

Be aware that setting a new header returns the new header object instead of manipulating your existing one. It’s immutable.

Interceptors

From AngularJS, we know HTTP interceptors are a great and very mighty way to observe ingoing and outgoing requests. In Angular, this feature of interceptors was not made available. But it came back with this new version of the HTTP interface.

Let us add an interceptor which is just logging the requests. This is just a class tagged with the @Injectable decorator implementation, and the interface HttpInterceptor. It gets passed as an HttpHeader which provides us with the handle(...) method to not cancel the request but just hook ‘between’ the application and the outgoing or incoming request. So the request has to be handled further from the next interceptor (chaining) or the backend.

import { Injectable} from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';

@Injectable()
export class MyFirstInterceptor implements HttpInterceptor {
    intercept(req: HttpRequestany, next: HttpHandler): ObservableHttpEventany {
        console.log(req);
        return next.handle(req);
    }
}

And, of course, we have to register it on the module:

providers: [
    // ...
    {
        provide: HTTP_INTERCEPTORS,
        useClass: MyFirstInterceptor,
        multi: true,
    }]

If we just log this one, we get a result like this:

{
"url": "http://blabla",
"body": null,
"reportProgress": false,
"withCredentials": false,
"responseType": "json",
"method": "GET",
"headers": {
"normalizedNames": {},
"lazyUpdate": null,
"headers": {}
},
"params": {
"updates": null,
"cloneFrom": null,
"encoder": {},
"map": null
},
"urlWithParams": "http://blablawithparams"
}

Now, we can tweak the interceptor to add the headers accordingly and add the authentication token if the user has one.

@Injectable()
export class MyFirstInterceptor implements HttpInterceptor {

    constructor(private currentUserService: CurrentUserService) { }

    intercept(req: HttpRequestany, next: HttpHandler): ObservableHttpEventany {

        // get the token from a service
        const token: string = this.currentUserService.token;

        // add it if we have one
        if (token) {
            req = req.clone({ headers: req.headers.set('Authorization', 'Bearer ' + token) });
        }

        // if this is a login-request the header is 
        // already set to x/www/formurl/encoded. 
        // so if we already have a content-type, do not 
        // set it, but if we don't have one, set it to 
        // default -- json
        if (!req.headers.has('Content-Type')) {
            req = req.clone({ headers: req.headers.set('Content-Type', 'application/json') });
        }

        // setting the accept header
        req = req.clone({ headers: req.headers.set('Accept', 'application/json') });
        return next.handle(req);
    }
}

With this, we can get rid of the method in the class above and we don’t need to call the method private prepareHeader(headers: HttpHeaders | null): object to prepare the headers. So the final result could look like:

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';

import { CurrentUserService } from '../services/currentUser.service';

@Injectable()
export class MyDataService {
    constructor(private http: HttpClient, private currentUserService: CurrentUserService) { }

    getT(url: string): ObservableT {
        return this.http.getT(url);
    }

    postT(url: string, body: string): ObservableT {
        return this.http.postT(url, body);
    }

    putT(url: string, body: string): ObservableT {
        return this.http.putT(url, body);
    }

    deleteT(url: string): ObservableT {
        return this.http.deleteT(url);
    }

    patchT(url: string, body: string): ObservableT {
        return this.http.patchT(url, body);
    }
}


@Injectable()
export class MyFirstInterceptor implements HttpInterceptor {

    constructor(private currentUserService: CurrentUserService) { }

    intercept(req: HttpRequestany, next: HttpHandler): ObservableHttpEventany {
        console.log(JSON.stringify(req));

        const token: string = this.currentUserService.token;

        if (token) {
            req = req.clone({ headers: req.headers.set('Authorization', 'Bearer ' + token) });
        }

        if (!req.headers.has('Content-Type')) {
            req = req.clone({ headers: req.headers.set('Content-Type', 'application/json') });
        }

        req = req.clone({ headers: req.headers.set('Accept', 'application/json') });
        return next.handle(req);
    }
}

I hope this article helped to give you a basic understanding of the new, neat HTTP API from Angular. I like it a lot and this code looks nicer and cleaner.

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*

cover letter