Angular ~ Http - rohit120582sharma/Documentation GitHub Wiki

Table of contents

  • Introduction
    • The HttpClientModule in @angular/common/http offers a simplified client HTTP API for Angular applications.
  • Request
    • Methods
      • get
      • post
      • put
      • delete
      • request
        • To deal with progress events
        • It takes new HttpRequest('<method>', '<url>', {<options>}) as a parameters
    • Configuration ({})
      • observe: 'response/body/events'
      • responseType: 'text/json/blob'
      • headers: new HttpHeaders().set('Authorization', 'access_token')
      • params: new HttpParams().set('customProp', 'foo')
      • reportProgress: true/false
      • withCredentials: true/false
  • Response
    • Observable/Promise is object to deal with asynchronous operations
    • Transform response
    • Headers
    • Body
    • Events
  • Error Handler
  • Interceptor
    • Introduction
      • Interceptors provide a mechanism to intercept every outgoing request or incoming response.
    • Create an interceptor
      • HttpInterceptor interface
      • Intercept request
      • Intercept response
    • Register an interceptor

Introduction

In JavaScript making HTTP requests is an asynchronous operation. It just sends the HTTP request to the API and when the API responds later then we get notified and we can start processing the response.

Angular comes with its own HTTP client library built on top of the XMLHttpRequest interface which we can use to make those calls.

It supports strong typing of request and response objects and request and response interceptor.

We can use Promises or Observables to handle these asynchronous operations.

References


Providing the Http dependencies

The http client is a service which lives in the @angular/common/http module along side a number of other classes which are helpful when working with http.

We inject the Http client library into our classes, it’s a dependency that needs to be configured in Angulars DI framework.

Rather than separately setup each provider for all the different parts of the Http client library we can instead import the HttpClientModule and add to our NgModule imports list. Importing HttpClientModule into our NgModule configures our NgModules injector with all the providers needed to use Http in our app.

import { HttpClientModule } from '@angular/common/http';
@NgModule({
	imports: [ HttpClientModule ]
});

HTTP Verbs

We make requests for all the HTTP verbs by calling matching functions on our Http client library.

To perform a GET request we simply call the get function on our http client.

To perform a DELETE request we just call the delete function.

To perform a POST request we call the post function. The second parameter to post is an object which will be passed as the payload for the request.

To perform a PUT request we just call the put function. It works in exactly the same was the post function.

All these methods are in fact just syntactic sugar, and they all call the method request under the hood.

We can pass a set of optional query parameters as an argument as well. This is in the format of an object like structure but to help in creating our query params object we can use the helper HttpParams class.

By default, the HttpClient returns the body of the response. You can pass in an object with an observe key set to a value of response to get the full response. This can be useful to inspect for certain headers.

No longer have to do .json() on responses to get the data since that’s the default now. If you expect something else than JSON as the response, you can specify the expected response type using an object with the responseType key.

import { HttpClient, HttpParams } from '@angular/common/http';
.
.
.
constructor(private http:HttpClient){
}
doGET(){
	let url = `${this.apiRoot}/get`;
	this.http.get(url, {observe: 'response', responseType: 'json'}).subscribe((res)=>{
		console.log(res.headers.get('X-Powered-By'));
		console.log(res.body);
	});
}
doDELETE(){
	let url = `${this.apiRoot}/delete`;
	this.http.delete(url).subscribe(res => console.log(res));
}
doPOST(){
	let url = `${this.apiRoot}/post`;
	this.http.post(url, {moo:"foo", goo:"loo"}).subscribe(res => console.log(res));
}
doPUT(){
	let url = `${this.apiRoot}/put`;
	this.http.put(url, {moo:"foo", goo:"loo"}).subscribe(res => console.log(res));
}
doGETWithQueryParam(){
	let url = `${this.apiRoot}/get`;
	let search = new HttpParams();
	search.set('foo', 'moo');
	search.set('limit', 25);
	this.http.get(url, {search}).subscribe(res => console.log(res));
}

Promises

By default, these requests return observables which we can subscribe to.

We can also convert these observables into promises and handle the asynchronous responses that way.

We just call toPromise() on the observable that gets returned and this will convert it into a promise.

The toPromise() function is an observable operator, so we need to import it before use.

import 'rxjs/add/operator/toPromise';
.
.
.
doGETAsPromise(){
	let url = `${this.apiRoot}/get`;
	this.http
		.get(url)
		.toPromise()
		.then(
			(res)=>{ console.log(res); }
		);
}

If we can import all RxJS operators, but the below will unnecessarily bloat the application so is not recommended for production.

import 'rxjs/RX';

Handling errors

Like we handle the response as an Observable or as a Promise, we can add error handling function as the second parameter.

In Promise, an error handler is the second argument to then.

In Observables, an error handler is the second argument to subscribe.

The err parameter to the callback is of type HttpErrorResponse, and contains useful information on what went wrong.

doGETAsPromiseError(){
	let url = `${this.apiRoot}/get`;
	this.http
		.get(url)
		.toPromise()
		.then(
			(res)=>{ console.log(res); },
			(err:HttpErrorResponse)=>{
				if(err.error instanceof Error){
					console.log('An error occurred:', err.error.message);
				}else{
					console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
				}
			}
		);
}

Headers

HTTP headers are bits of meta-data which the browser attaches to your HTTP requests when it sends them to the server. Things like your IP address, the type of browser you are using and so on are added to your headers.

The headers option is often useful to add a few custom headers to your request. It happens to be necessary for some authentication techniques like JSON Web Token.

To send custom headers with our requests, we have to follow steps:

  • First need to import helper classes from the HTTP module.
  • Create an instance of headers and add our specific header to the mix. The HttpHeaders class is immutable, so every set() returns a new instance and applies the changes.
  • Finally we pass the options to the appropriate http function.
import { HttpHeaders, HttpErrorResponse } from '@angular/common/http';
.
.
.
doGETWithHeaders(){
	// Create options
	let opts = {
		headers: new HttpHeaders().set('Authorization', 'my-auth-token')
	};

	// request
	let url = `${this.apiRoot}/get`;
	this.http
		.get(url, opts)
		.subscribe(
			(res)=>{ console.log(res); },
			(err:HttpErrorResponse)=>{ console.error(`Error: ${err.status} ${err.statusText}`); }
		);
}

Interceptor

Introduction

Interceptors provide a mechanism to intercept and/or mutate outgoing requests or incoming responses. They are very similar to the concept of middleware with a framework like Express, except for the frontend.

When your application makes a request, interceptors transform it before sending it to the server, and the interceptors can transform the response on its way back before your application sees it. This is useful for everything from authentication to logging.

Setup

The new HTTP client resides in the @angular/common/http package. You need to register the HttpClientModule and register the interceptor on the HTTP_INTERCEPTORS.

The multi: true option is required and tells Angular that HTTP_INTERCEPTORS is an array of values, rather than a single value.

The interceptors will be called in the order in which they were provided. So with the above, MyInterceptor would handle http requests first.

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
// your interceptor files
import { MyInterceptor } from './my.interceptor';
import { MyHttpLogInterceptor } from './http.interceptor';

@NgModule({
	imports: [ ..., HttpClientModule ],
	providers: [
		{ provide: HTTP_INTERCEPTORS, useClass: MyHttpLogInterceptor, multi: true }  
	],
	...
})
export class AppModule {}

Writing an interceptor

An HTTP interceptor is just an Angular service implementing a specific interface, the HttpInterceptor.

The class should define an intercept method to correctly implement HttpInterceptor.

The intercept method takes two arguments, req and next, and returns an observable of type HttpEvent.

  • req is the request object itself and is of type HttpRequest.
  • next is an HttpHandler. The handler has a handle method that returns our desired HttpEvent observable. Most of the time, though, interceptors will make some minor change to the request and forward it to the rest of the chain. That's where the next parameter comes in.
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest, HttpResponse, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';

@Injectable()
export class MyInterceptor implements HttpInterceptor {
	intercept(req:HttpRequest<any>, next:HttpHandler):Observable<HttpEvent<any>>{
		return next
				.handle(request)
				.do(evt=>{
					if(evt instanceof HttpResponse){
						console.log('---> status:', evt.status);
						console.log('---> filter:', req.params.get('filter'));
					}
				});
	}
}

Intercepting HTTP requests and responses

HttpRequest objects are immutable, so in order to modify them, we need to first make a copy, then modify the copy and call handle on the modified copy.

To intercept the response coming back from the server, we can simply hook on the do(..) operator as the new HTTP module heavily relies on the Observable API.

import { HttpInterceptor, HttpRequest, HttpResponse, HttpErrorResponse, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

@Injectable()
export class MyInterceptor implements HttpInterceptor {
	intercept(req:HttpRequest<any>, next:HttpHandler):Observable<HttpEvent<any>>{
		// add a custom header
		const customReq = req.clone({
			headers: req.headers.set('app-language', 'it')
		});

		// pass on the modified request object
		return next
				.handle(customReq)
				.do((evt:HttpEvent<any>)=>{
					if(evt instanceof HttpResponse){
						console.log('processing response', evt);
					}
				})
				.catch((response)=>{
					if (response instanceof HttpErrorResponse) {
						console.log('Processing http error', response);
					}
					return Observable.throw(response);
				});
	}
}

Progress Events

A great new feature with HttpClient is the ability to listen for progress events. The different information will be available during the lifecycle of the request event.

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

@Injectable()
export class DataService{
	constructor(private http:HttpClient){
	}
	getData(){
		let url = '/some/api';
		const req = new HttpRequest('GET', url, {reportProgress: true});
		this.http
				.request(req)
				.subscribe((event:HttpEvent<any>)=>{
					switch(event.type){
						case HttpEventType.Sent:
							console.log('Request sent!');
							break;
						case HttpEventType.ResponseHeader:
							console.log('Response header received!');
							break;
						case HttpEventType.DownloadProgress:
							const kbLoaded = Math.round(event.loaded / 1024);
							console.log(`Download in progress! ${ kbLoaded }Kb loaded`);
							break;
						case HttpEventType.Response:
							console.log('Done!', event.body);
							break;
					}
				});
	}
}

⚠️ **GitHub.com Fallback** ⚠️