Introducing the HTTP API

The Http class provides a powerful API that abstracts all the operations required to handle asynchronous connections through a variety of HTTP methods, handling the responses in an easy and comfortable way. Its implementation has been made with a lot of care to ensure that programmers will feel at ease while developing solutions that take advantage of this class to connect to an API or a data resource.

In a nutshell, instances of the Http class (which has been implemented as an Injectable resource and can therefore be used in our classes constructors just by injecting it as a dependency provider) expose a connection method named request() to perform any type of http connection. The Angular 2 team has created some syntax shortcuts for the most common request operations, such as GET, POST, PUT, and every existing HTTP verb. So, creating an async Http request is as easy as this:

var requestOptions = new RequestOptions({
  method: RequestMethod.Get,
  url: '/my-api/my-data-store.json'
});
var request = new Request(requestOptions);
var myHttpRequest: Observable<Response> = http.request(request);

Also, all of this can be simplified into a single line of code:

var myHttpRequest: Observable<Response> = http.get('/my-api/my-data-store.json');

As we can see, the Http class connection methods operate by returning an Observable stream of Response object instances. This allows us to map and digest the output of the Http call as soon as it is available and subscribe Observers to the stream, which will process the information accordingly once it is returned, as many times as required:

myHttpRequest.map(response: Response => response.json())
  .subscribe(data => console.log(data));

In the preceding example, we map the responses emitted by the Observable event stream (as we can see, those are basically instances of the Response class provided by Angular 2) to a new collection of events representing JSON instances of each response. This is done by leveraging the json() method of the Response class, which we will cover later on in this chapter. Then we subscribe to the resulting stream and throw the output of the HTTP call to the Console once our Observer receives the data notification.

By doing this, we can respawn the Http request as many times as we need, and the rest of our machinery will react accordingly. We can even merge the event stream represented by the Http call with other related calls, and compose more complex Observable streams and data threads. The possibilities are endless.

When to use the Request and RequestOptionsArgs classes

We mentioned the Request and RequestOptions classes while introducing the Http class at first. On a regular basis, you will not need to make use of these low-level classes, mostly because the shortcut methods provided by the Http class abstract the need to declare the HTTP verb in use (GET, POST, and so on) and the URL we want to consume. With this being said, you will sometimes want to introduce special HTTP headers in your requests or append query string parameters automatically to each request, for argument's sake. That is why these classes can become quite handy in certain scenarios. Think of a use case where you want to add an authentication token to each request in order to prevent unauthorized users from reading data from one of your API endpoints.

In the following example, we read an authentication token and append it as a header to our request to a data service. Contrary to our example, we will inject the options hash object straight into the Request constructor, skipping the step of creating a RequestOptions object instance. Angular 2 provides a wrapper class for defining custom headers as well, and we will take advantage of it in this scenario. Let's figure out that we do have an API that expects all requests to include a custom header named Authorization, attaching the authToken that we received when logging into the system, which was then persisted in the browser's local storage layer, for instance:

var authToken = window.localStorage.getItem('auth_token');
var headers = new Headers();
headers.append('Authorization', `Token ${authToken}`);

var request = new Request({
  method: RequestMethod.Get,
  url: '/my-api/my-secured-data-store.json',
  headers: headers
}); 

var authRequest: Observable<Response> = http.request(request);

Again, we would like to note that apart from this scenario, you will seldom need to create custom request configurations, unless you want to delegate the creation of request configurations in a factory class or method and reuse the same Http wrapper all the time. Angular 2 gives you all the flexibility to go as far as you wish when abstracting your applications.

The Response object

As we have already seen, the HTTP requests performed by the Http class return an observable stream of Response class instances. Similar to the Request object, you will rarely find yourself in need of instantiating this class. However, understanding the Response class interface is quite useful in order to understand the status of our request, handle connection errors, and properly digest the information returned in the stream.

In our first example, we mapped the content of the stream as a stream of JSON objects by executing the json() method. This method parses the response body as a JSON object, or raises an exception if such a body cannot be parsed. Besides this method, the Response class exposes the text() method, which will parse and return the response body as a plain string.

Let's figure this out: we have a component that exposes a string field named bio, which is rendered in the component's template. We might want to serve this bio property from a REST API. Hence, we could implement such functionality in a few lines of code, like this:

http.get('/api/bio')
  .map(res: Response => res.txt)
  .subscribe(bio => this.bio = bio);

The Response object exposes other minor methods and some interesting properties, such as the numeric status property, which informs of the status code returned by the server. The bytesLoaded and totalBytes numeric properties become quite useful when scaffolding preload notifiers in progress events. Special mention goes to the headers property, which returns an object based on the Headers class (https://fetch.spec.whatwg.org/#headers-class) of the Fetch API.

Coverage for all the methods and properties available in the Response class API is definitely beyond the scope of this book, as you will not be using those on a regular basis, but we encourage you to expand your knowledge on the subject by visiting the official documentation (https://angular.io/docs).

Handling errors when performing Http requests

Handling errors raised in our requests by inspecting the information returned in the Response object is actually quite simple. We just need to inspect the value of its Boolean property, which will return false if the HTTP status of the response falls somewhere out side of the 2xx range, clearly indicating that our request could not be accomplished successfully. We can double-check that by inspecting the status property to understand the error code or the type property, which can assume the following values: basic, cors, default, error, or opaque. Inspecting the response headers and the statusText property of the Response object will provide insightful information about the origin of the error.

All in all, we are not meant to inspect those properties on every response message we get. Angular 2 provides an Observable operator to catch errors, injecting in its signature the Response object we require to inspect the previous properties:

http.get('/api/bio')
  .map(res: Response => res.txt)
  .subscribe(bio => this.bio = bio)
  .catch(error: Response => console.error(error));

In a normal scenario, you would want to inspect more data rather than the error properties, aside from logging that information in a more solid exception tracking system.

Injecting the Http class and the HTTP_PROVIDERS modules symbol

The Http class can be injected in our own components and custom classes by leveraging Angular's unique dependency injection system. So, if we ever need to implement HTTP calls, we need to import the class and bind it as a dependency in the list of component or directive providers, like this:

import { Component } from '@angular/core';
import { Http } from '@angular/http';

@Component({
  selector: 'bio',
  providers: [Http],
  template: '<div>{{bio}}</div>'
})
class Biography {
  bio: string;
  constructor(http: Http) {
    http.get('/api/bio')
      .map((res: Response) => res.text())
      .subscribe((bio: string) => this.bio = bio);
  }
}

In the code provided, we just follow up with the bio example that we pointed out in the previous section. Note how we are importing the Http type and injecting it as a dependency through the providers collection property of the Component decorator.

Usually, we need to perform multiple HTTP calls in different parts of our application, so it's usually recommended to include the Http class in the root injector rather than on a per component injector basis. To do so and keeping in mind that the Http class might require other providers such as the RequestOptions class, Angular 2 provides a set of injectable symbols wrapped inside the HTTP_PROVIDERS token.

We recommend that you use this set instead of the Http class to inject the dependencies required to perform HTTP requests throughout your application. For your convenience, it is advised to do so at the root component providers property, so its injector will make the provider available throughout its child components tree. Taking our pomodoro app as an example, we would need to import it at the AppComponent code unit and inject it into the component providers right away:

app/app.component.ts

import { Component } from '@angular/http';
import { TIMER_DIRECTIVES } from './timer/timer';
import { TASKS_DIRECTIVES } from './tasks/tasks';
import { SHARED_PROVIDERS } from './shared/shared';
import { HTTP_PROVIDERS } from '@angular/http';

@Component({
  selector: 'pomodoro-app',
  directives: [TIMER_DIRECTIVES, TASKS_DIRECTIVES],
  providers: [SHARED_PROVIDERS, HTTP_PROVIDERS],
  ...
})
export default class AppComponent {}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset