ChatGPT解决这个技术问题 Extra ChatGPT

Angular2 handling http response

I just have a question regarding structuring and handling responses from http requests within a service. I am using Angular2.alpha46 Typescript ( Just started testing it out- which I love... Ps.. Thank you all the people who have been working on it and contributing via github )

So take the following:

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

from this component I import my userService which will house my http request to login the user the service looks like this:

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

What I want to do is to be able to handle the response the call returns after the http request. For instance if the user credentials are invalid I pass a 401 response back from the backend. My question is where is the best way to handle the response and return the result back to the component where i called the method from so I can manipulate the view to show either the success message or display an error message.

At the moment in my service under login I am currently not handling the response I am simply doing a callback back to the original component but I feel this isnt the correct way to go about it? Can someone shed some light on what they would do in this typical scenario? Would I handle the response in the first parameter of the subscribe function like:

 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'
}

Is a call back fine in this case or can this be handled better with an observable or a promise?

Concluding what I am asking is... What is the best practice to handle the response from the http response and handle the status in the view of the form from the user.service.ts back to the login-form.component.ts

In the next release (see this commit) Http will error when you get non-200 status codes. For now you can handle the response in map(), check the status there and throw an Error or pass the value.
Thanks for the swift reply Eric. Cool! I thought that and I am aware that some stuff is still up for discussion... So just to confirm... Would you say its completely find to handle the response status in map for now and pass the value in a callback? Could you do it in a better manner or is a call back suffice?
You don't need the callback at all, see my answer and it will hopefully answer your question :D
Awesome I totally understand from the commit and plunker what to do to handle the header status but I am just wondering whats the best way to pass back the error or the success data back to the original component I called the method from so I can assign it to a variable and bind it on the view. Yourexample is awesome but you are calling the http request on that component so you can assign the error or the success data in the same component because its context is shared; but how can I process the response in the service and then when I am done pass a response and code logic to succeed or fail
I would move that logic out of the service. In the service I would just do return http.post().map(/* handle result */), and then in my component I would do this.userService.login(...).subscribe(/* handle data*/)

E
Eric Martinez

Update alpha 47

As of alpha 47 the below answer (for alpha46 and below) is not longer required. Now the Http module handles automatically the errores returned. So now is as easy as follows

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

Alpha 46 and below

You can handle the response in the map(...), before the 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

Here's a plnkr with a simple example.

Note that in the next release this won't be necessary because all status codes below 200 and above 299 will throw an error automatically, so you won't have to check them by yourself. Check this commit for more info.


plnkr you have linked is not working properly please fix it. i need to get the status code while REST API hitting how can i get this ?
@PardeepJain what do you mean by not working? If you want to make the request to fail just comment out the first get, and uncomment the second one.
what if i want to return both res.status and res.json() together ?
return {status: res.status, json: res.json()}
im on angular2 v2.1.1 and this method does not trigger error section when server blows up. i see the exception written out in console by zone.js. I do need to intercept it to provide some feedback in the UI
S
Sonic Soul

in angular2 2.1.1 I was not able to catch the exception using the (data),(error) pattern, so I implemented it using .catch(...).

It's nice because it can be used with all other Observable chained methods like .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

from documentation:

Returns (Observable): An observable sequence containing elements from consecutive source sequences until a source sequence terminates successfully.


Question: can the observable be something else that is not an error? Otherwise, if I'm rethrowing the error again what's the point?
not sure what you mean. observable is not an error. normally it will carry the data objects which can be subscribed to. if error happens, it will trigger the catch block. in that case, a throw has to happen so that the chain can continue. it is up to other subscribers to decide how to handle it. For example you can subscribe to this result in other places, and they may want to handle errors differently.
I understand now. That throw word had me confused. But I see now the benefit of it. Thanks
b
bArraxas

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

In the app :

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

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now