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
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.
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.
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) }
)