Angular2处理http响应

Posted

技术标签:

【中文标题】Angular2处理http响应【英文标题】:Angular2 handling http response 【发布时间】:2016-02-29 17:42:51 【问题描述】:

我只是有一个关于在服务中构建和处理来自 http 请求的响应的问题。我正在使用 Angular2.alpha46 Typescript(刚开始测试它——我喜欢它...... Ps.. 谢谢所有一直致力于它并通过 github 做出贡献的人)

所以采取以下措施:

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

我从这个组件导入了我的 userService,它将容纳我的 http 请求以登录用户,该服务如下所示:

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);
    

我想要做的是能够处理在 http 请求之后调用返回的响应。例如,如果用户凭据无效,我会从后端传回 401 响应。我的问题是处理响应并将结果返回到我从中调用方法的组件的最佳方法是哪里,以便我可以操纵视图以显示成功消息或显示错误消息。

目前在我的登录服务中,我目前没有处理响应我只是在回调原始组件,但我觉得这不是正确的方法吗?有人可以阐明他们在这种典型情况下会做什么吗?我会在订阅函数的第一个参数中处理响应吗,例如:

 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'

在这种情况下回调是否正常,或者可以通过 observable 或 promise 更好地处理?

总结我的要求是......处理来自http响应的响应并处理来自 user.service.ts 的表单视图中的状态的最佳实践是什么?到 login-form.component.ts

【问题讨论】:

在下一个版本中(请参阅this commit)Http 将在您获得非 200 状态代码时出错。现在您可以处理map() 中的响应,检查那里的状态并抛出错误或传递值。 感谢 Eric 的快速回复。凉爽的!我是这么想的,我知道有些东西还有待讨论......所以只是为了确认......你会说它现在完全找到处理地图中的响应状态并在回调中传递值吗?您能以更好的方式做到这一点还是回电就足够了? 您根本不需要回调,请参阅我的回答,它有望回答您的问题:D 太棒了我从提交和 plunker 中完全理解如何处理标题状态,但我只是想知道将错误或成功数据传回给我调用的原始组件的最佳方法是什么方法 from 所以我可以将它分配给一个变量并将它绑定到视图上。 Yourexample 很棒,但是您在该组件上调用 http 请求,因此您可以在同一组件中分配错误或成功数据,因为它的上下文是共享的;但是如何在服务中处理响应,然后当我完成时传递响应和代码逻辑以成功或失败 我会将该逻辑移出服务。在服务中我只会做return http.post().map(/* handle result */),然后在我的组件中我会做this.userService.login(...).subscribe(/* handle data*/) 【参考方案1】:

服务:

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

在应用中:

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

【讨论】:

【参考方案2】:

在 angular2 2.1.1 中,我无法使用 (data),(error) 模式捕获异常,因此我使用 .catch(...) 实现了它。

这很好,因为它可以与所有其他 Observable 链式方法一起使用,例如 .retry .map 等。

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

来自documentation:

返回

(Observable):一个可观察序列,包含来自连续源序列的元素,直到源序列成功终止。

【讨论】:

问题:observable 可以是其他不是错误的东西吗?否则,如果我再次抛出错误有什么意义? 不确定您的意思。 observable 不是错误。通常它会携带可以订阅的数据对象。如果发生错误,它将触发 catch 块。在这种情况下,必须进行一次投掷,这样链才能继续。由其他订阅者决定如何处理它。例如,您可以在其他地方订阅此结果,他们可能希望以不同的方式处理错误。 我现在明白了。那个投掷词让我感到困惑。但我现在看到了它的好处。谢谢【参考方案3】:

更新 alpha 47

从 alpha 47 开始,不再需要以下答案(对于 alpha46 及以下)。现在 Http 模块自动处理返回的错误。所以现在就这么简单

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

Alpha 46 及以下

您可以在subscribe 之前处理map(...) 中的响应。

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

这是plnkr 的一个简单示例。

请注意,在下一个版本中,这将不是必需的,因为所有低于 200 和高于 299 的状态代码都会自动抛出错误,因此您不必自己检查它们。查看此commit 了解更多信息。

【讨论】:

您链接的plnkr无法正常工作,请修复它。我需要在 REST API 命中时获取状态码我怎么能得到这个? @PardeepJain 不工作是什么意思?如果您想让请求失败,只需注释掉第一个 get,然后取消注释第二个。 如果我想同时返回 res.status 和 res.json() 怎么办? return status: res.status, json: res.json() im 在 angular2 v2.1.1 上,当服务器崩溃时,此方法不会触发错误部分。我看到 zone.js 在控制台中写出的异常。我确实需要拦截它以在 UI 中提供一些反馈

以上是关于Angular2处理http响应的主要内容,如果未能解决你的问题,请参考以下文章

获取状态码 http.get 响应 angular2

如果 Angular2 HTTP 响应数据不符合类型,我如何捕获错误

Angular 2,轮询 http 请求并等待缓慢的响应

Angular2 HTTP GET - 将响应转换为完整对象

Angular 2:如何访问 HTTP 响应正文?

通过基本身份验证访问 REST 的 Angular2 抛出“预检响应具有无效的 HTTP 状态代码 401”