web-dev-qa-db-fra.com

Angular2 traitant la réponse http

J'ai juste une question concernant la structuration et le traitement des réponses des requêtes http au sein d'un service. J'utilise Angular2.alpha46 TypeScript (Je viens juste de commencer à le tester - ce que j'adore ... Ps .. Merci à tous ceux qui y ont travaillé et qui ont contribué via github)

Alors prenez ce qui suit:

login-form.component.ts

import {Component, CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/angular2';
import {UserService} from '../../shared/service/user.service';
import {Router} from 'angular2/router';
import {User} from '../../model/user.model';
import {APP_ROUTES, Routes} from '../../core/route.config';

@Component({
    selector: 'login-form',
    templateUrl: 'app/login/components/login-form.component.html',
    directives: [CORE_DIRECTIVES, FORM_DIRECTIVES]
})

export class LoginFormComponent {
    user: User;
    submitted: Boolean = false;

    constructor(private userService:UserService, private router: Router) {
        this.user = new User();
    }

    onLogin() {
        this.submitted = true;

        this.userService.login(this.user,
            () => this.router.navigate([Routes.home.as]))
    }
}

à partir de ce composant, j'importe mon userService qui hébergera ma requête http de connexion à l'utilisateur. Le service ressemble à ceci:

ser.service.ts

import {Inject} from 'angular2/angular2';
import {Http, HTTP_BINDINGS, Headers} from 'angular2/http';
import {ROUTER_BINDINGS} from 'angular2/router';
import {User} from '../../model/user.model';

export class UserService {

    private headers: Headers;

    constructor(@Inject(Http) private http:Http) {
    }

    login(user: User, done: Function) {
        var postData = "email=" + user.email + "&password=" + user.password;

        this.headers = new Headers();
        this.headers.append('Content-Type', 'application/x-www-form-urlencoded');

        this.http.post('/auth/local', postData, {
                headers: this.headers
            })
            .map((res:any) => res.json())
            .subscribe(
                data => this.saveJwt(data.id_token),
                err => this.logError(err),
                () => done()
            );
    }

    saveJwt(jwt: string) {
        if(jwt) localStorage.setItem('id_token', jwt)
    }

    logError(err: any) {
        console.log(err);
    }
}

Ce que je veux faire, c'est pouvoir traiter la réponse que l'appel renvoie après la requête http. Par exemple, si les informations d'identification de l'utilisateur ne sont pas valides, je renvoie une réponse 401 du serveur. Ma question est la suivante: quel est le meilleur moyen de gérer la réponse et de renvoyer le résultat au composant d'où j'ai appelé la méthode afin que je puisse manipuler la vue pour afficher le message de réussite ou pour afficher un message d'erreur.

Pour le moment, dans mon service en cours de connexion, je ne gère pas actuellement la réponse. Je fais simplement un rappel vers le composant d'origine, mais j'estime que ce n'est pas la bonne façon de procéder. Quelqu'un peut-il nous éclairer sur ce qu'ils feraient dans ce scénario typique? Aurais-je gérer la réponse dans le premier paramètre de la fonction subscribe comme:

 login(user: User, done: Function) {
     var postData = "email=" + user.email + "&password=" + user.password;

    this.headers = new Headers();
    this.headers.append('Content-Type', 'application/x-www-form-urlencoded');

    this.http.post('/auth/local', postData, {
            headers: this.headers
        })
        .map((res:any) => res.json())
        .subscribe(
            (data) => {
                // Handle response here
                let responseStat = this.handleResponse(data.header)

                // Do some stuff
                this.saveJwt(data.id_token);

                // do call back to original component and pass the response status
                done(responseStat);
            },
            err => this.logError(err)
        );
}

handleResponse(header) {
    if(header.status != 401) {
        return 'success'
    } 

    return 'error blah blah'
}

Dans ce cas, un rappel est-il correct ou cela peut-il être mieux traité avec un observable ou une promesse?

En conclusion, ce que je demande, c’est ... Quelle est la meilleure pratique pour gérer la réponse de la réponse http et gérer le statut dans l’affichage du formulaire à partir de ser.service.ts retour à - login-form.component.ts

68
Nige

Update alpha 47

À partir de l'alpha 47, la réponse ci-dessous (pour l'alpha46 et inférieur) n'est plus requise. Maintenant, le module Http gère automatiquement les erreurs renvoyées. Alors maintenant, c'est aussi simple que suit

http
  .get('Some Url')
  .map(res => res.json())
  .subscribe(
    (data) => this.data = data,
    (err) => this.error = err); // Reach here if fails

Alpha 46 et inférieur

Vous pouvez gérer la réponse dans la map(...), avant la subscribe.

http
  .get('Some Url')
  .map(res => {
    // If request fails, throw an Error that will be caught
    if(res.status < 200 || res.status >= 300) {
      throw new Error('This request has failed ' + res.status);
    } 
    // If everything went fine, return the response
    else {
      return res.json();
    }
  })
  .subscribe(
    (data) => this.data = data, // Reach here if res.status >= 200 && <= 299
    (err) => this.error = err); // Reach here if fails

Voici un plnkr avec un exemple simple.

Notez que dans la prochaine version, cela ne sera pas nécessaire, car tous les codes d’état inférieurs à 200 et supérieurs à 299 généreront automatiquement une erreur. Vous n’avez donc pas à les vérifier par vous-même. Vérifiez ceci commit pour plus d'informations.

87
Eric Martinez

in angular2 2.1.1 Je ne pouvais pas attraper l'exception en utilisant le modèle (data), (error), je l'ai donc implémentée en utilisant .catch (...).

C'est agréable car il peut être utilisé avec toutes les autres méthodes chaînées observables comme .retry .map etc.

import {Observable} from 'rxjs/Rx';


  Http
  .put(...)
  .catch(err =>  { 
     notify('UI error handling');
     return Observable.throw(err); // observable needs to be returned or exception raised
  })
  .subscribe(data => ...) // handle success

de documentation :

Résultats

(Observable): une séquence observable contenant des éléments de séquences sources consécutives jusqu'à ce qu'une séquence source se termine avec succès.

11
Sonic Soul

Le service :

import 'rxjs/add/operator/map';

import { Http } from '@angular/http';
import { Observable } from "rxjs/Rx"
import { Injectable } from '@angular/core';

@Injectable()
export class ItemService {
  private api = "your_api_url";

  constructor(private http: Http) {

  }

  toSaveItem(item) {
    return new Promise((resolve, reject) => {
      this.http
        .post(this.api + '/items', { item: item })
        .map(res => res.json())
        // This catch is very powerfull, it can catch all errors
        .catch((err: Response) => {
          // The err.statusText is empty if server down (err.type === 3)
          console.log((err.statusText || "Can't join the server."));
          // Really usefull. The app can't catch this in "(err)" closure
          reject((err.statusText || "Can't join the server."));
          // This return is required to compile but unuseable in your app
          return Observable.throw(err);
        })
        // The (err) => {} param on subscribe can't catch server down error so I keep only the catch
        .subscribe(data => { resolve(data) })
    })
  }
}

Dans l'application:

this.itemService.toSaveItem(item).then(
  (res) => { console.log('success', res) },
  (err) => { console.log('error', err) }
)
4
bArraxas