Home
Non solo interception

02 Novembre 2017

Non solo interception

di

Un nuovo modulo per trattare HTTP che colma una lacuna riconosciuta dagli sviluppatori con esperienza su Angular.

Sono trascorsi diversi mesi dall’uscita di Sviluppare applicazioni con Angular – Guida alla programmazione web e mobile e sono soddisfatto del libro, perché il suo contenuto risulta ancora attualissimo alla prova di Angular com’è oggi.

Confrontando la versione di Angular utilizzata durante la stesura (4.2.x) con quella attuale si possono comunque constatare alcune novità di rilievo, una delle quali è il modulo HttpClient, presente dalla release 4.3.0.

Nonostante HttpClient soppianti il precedente modulo Http, il suo utilizzo consente una migrazione abbastanza indolore. Questo perché i due moduli attualmente coesistono e solo successivamente Angular provvederà a deprecare HttpModule.

HttpClient, dal mio personalissimo punto di vista, è davvero un’importante novità poiché sopperisce ad una mancanza che i vecchi utilizzatori di AngularJS certamente riconoscevano come grave. Parlo di un interceptor.

L’acchiappaprocessi

L’interceptor è uno strumento nato per intercettare e manipolare un determinato processo di elaborazione. Nel nostro caso, Interceptor viene associato ad una chiamata HTTP, inserendo così uno strato intermedio che consente di alterare la chiamata, in un verso o nell’altro, prima che questa venga effettivamente elaborata.

L’esempio più tipico è certamente l’interceptor in relazione all’autenticazione basata su JWT.

Il Web Token JSON consente lo scambio sicuro d’informazioni tra client e server, nella fattispecie l’identificazione di un determinato utente attraverso l’ausilio di un token.

L’aspetto chiave di questa tipologia di autenticazione è il suo essere stateless. L’autenticazione e di conseguenza le informazioni relative all’utente vengono detenute dal client e codificate all’interno di un token. Cosi facendo ogni richiesta HTTP sarà indipendente dalla precedente e verrà sempre caratterizzata dalla presenza del token (stateless).

Stateless, Stateful

Discutere di vantaggi e svantaggi relativi all’uso di questa tecnologia aprirebbe un capitolo a parte e non è il momento di affrontarlo in questo post. Vi invito comunque a documentarvi maggiormente sulle differenze tra stateless e stateful, nello specifico tra token e cookie, che semplificando moltissimo sono gli scenari in contrapposizione.

Passiamo all’aspetto pratico. Immaginiamo uno scenario in cui abbiamo un server che gestisce diversi endpoint privati e di conseguenza si aspetta un token nello header dalle richieste REST. Sulla parte client abbiamo invece un’applicazione Angular che ha mappato in un servizio tutte le chiamate successive alla login. Queste chiamate dovranno contenere lo header in questione. Interceptor è la soluzione.

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from
   '@angular/common/http';
import { AuthService } from './auth/auth.service';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(public auth: AuthService) {}
  intercept(request: HttpRequest, next: HttpHandler): Observable<HttpEvent> {

    request = request.clone({
      setHeaders: {
        Authorization: `Bearer ${this.auth.getToken()}`
      }
    });
    return next.handle(request);
  }
}

 

La classe AuthInterceptor implementa l’interfaccia HttpInterceptor, cosi facendo avremo a disposizione il metodo intercept, che è ciò che fa al caso nostro. I parametri richiesti da intercept sono: request, che corrisponde alla richiesta che viene elaborata al momento e next, che consente di passare al prossimo gestore, un altro Interceptor o direttamente alla chiamata http.

Sviluppare applicazioni con Angular

Nella battaglia per il web arrivano gli interceptor.

 

Il metodo clone di request non fa altro che clonare la chiamata originale affinchè possa essere manipolata dall’interceptor e ritornare quest’ultima. In questo specifico caso viene settato l’header Authorization con il token che viene restituito da getToken. Il token è preceduto dalla chiave Bearer, che identifica, come indicato dallo standard RFC 6750, appunto lo scambio di un token. Il metodo intercept infine ritorna un Observable, che corrisponderà alla risposta effettiva della chiamata Http.

Questo interceptor è abbastanza usuale per l’autenticazione JWT. La classe AuthService è solo un esempio, che nella fattispecie si occupa di gestire il token ricevuto presumibilmente in fase di login.

La classe AuthInterceptor deve essere chiaramente definita come provider in @NgModule.

import {HTTP_INTERCEPTORS} from '@angular/common/http';
@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: AuthInterceptor,
    multi: true,
  }],
})

 

HTTP_INTERCEPTORS definisce la classe AuthInterceptor come interceptor. L’opzione multi impostata a true consente di utilizzare un array di interceptor. Più interceptor definiti verranno eseguiti in cascata.

Il modulo HttpClient, oltre a quanto appena visto, offre altre interessanti novità. Diversamente da Http, HttpClient lavora con JSON come formato di default. Le chiamate http effettuate con Http erano caratterizzate dal metodo map, per manipolare la risposta e tornare un json alla subscribe.

return this.http.get(url)
                 .map((res:Response) => res.json())
                 

 

Possiamo sostituire questa sintassi con una più breve:

return this.http.get(url);

 

Nel caso in cui il dato ricevuto non sia un json, è possibile indicarne il tipo attraverso l’opzione responseType.

this.http.get(url, { responseType: 'text' })

 

È altresì possibile, nel caso in cui volessimo accedere ad un header specifico, indicare al modulo che vogliamo ottenere l’intero pacchetto http. Questo è possibile tramite l’opzione {observe: ‘response’}:

http.get('url', {observe: 'response'}).subscribe(res => {
    console.log(res.headers);
    console.log(res.body);
});

 

Altra novità è la possibilità di effetturare il type safety direttamente nella chiamata, come nell’esempio seguente.

interface Link {
  id: number;
  url: string;
}

this.http.get(this.url).subscribe(res => {
    this.id = res.id;
});

 

La gestione degli errori in HttpClient è demandata a HttpErrorResponse:

this.http.get(this.url).subscribe(res => {},
  (err: HttpErrorResponse) => {
    console.log(err);
  }
);

 

L’errore poträ essere generato in fase di esecuzione all’interno della subscribe, quindi scatenato lato client, oppure dall’endpoint (40x, 500 eccetera); in entrambi i casi l’interfaccia HttpErrorResponse consente di poter scindere la tipologia d’errore analizzandone il contenuto. Di norma la condizione che consente di scindere l’errore lato client da quello server consiste nel controllare se err.error sia una istanza della classe Error, generata in caso di errori a runtime.

I parametri delle richieste devono essere gestisti attraverso la classe HttpParams, cosi come gli header con la classe HttpHeaders.

const params = new HttpParams().set('name', 'vincenzo');
this.http.get(url, { params }).subscribe();

this.http.post(url, body, {
    headers: new HttpHeaders().set('Authorization', 'bearer token'),
  }).subscribe();
  

 

Un’altra particolarità è data dagli eventi progressivi. Nel caso in cui le nostre chiamate http, a causa di un alto volume di dati scambiati, rimanessero in attesa per più tempo, sarebbe utile ricevere un feedback continuo. Ecco a che cosa servono i progress event.

Per predisporre una chiamata di questo tipo va usato HttpRequest.

http.request(new HttpRequest('post', url, body, {
    reportProgress: true
})).subscribe(event => {
  if (event.type === HttpEventType.DownloadProgress) {}
  if (event.type === HttpEventType.UploadProgress) {}
  if (event.type === HttpEventType.Response) {}
})

 

Il sorgente è abbastanza leggibile: la request restituirà un Observable, che a sua volta gestito dalla subscribe genererà uno stream, il quale rende possibile gestire gli avanzamenti della chiamata post in questione.

Non ho specificato che il modulo HttpClient deve essere, chiaramente, implementato attraverso il modulo HttpClientModule:

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

@NgModule({
imports: [
  ..
  HttpClientModule
],
})

 

Per adesso è tutto su quanto di buono abbia introdotto HttpClient. Il resto va letto e studiato sulla documentazione ufficiale.

Accetto suggerimenti su argomenti Angular che volete vengano approfonditi. A rileggerci al prossimo appuntamento!

L'autore

  • Vincenzo Giacchina
    Vincenzo Giacchina lavora come Solution Architect in ambito finanziario. Appassionato di scrittura, collabora con le principali realtà del mondo underground e di settore dedicate alla divulgazione su temi tecnici e informatici. Attivo anche nel campo della formazione, segue costantemente l'evoluzione delle tecnologie full stack, con particolare attenzione al mondo web e mobile. Vive in Svizzera.

Iscriviti alla newsletter

Novità, promozioni e approfondimenti per imparare sempre qualcosa di nuovo

Gli argomenti che mi interessano:
Iscrivendomi dichiaro di aver preso visione dell’Informativa fornita ai sensi dell'art. 13 e 14 del Regolamento Europeo EU 679/2016.