如何从 http.request() 中正确捕获异常?

Posted

技术标签:

【中文标题】如何从 http.request() 中正确捕获异常?【英文标题】:How to catch exception correctly from http.request()? 【发布时间】:2016-05-21 11:35:22 【问题描述】:

我的部分代码:

import Injectable from 'angular2/core';
import Http, Headers, Request, Response from 'angular2/http';
import Observable from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Injectable()
export class myClass 

  constructor(protected http: Http) 

  public myMethod() 
    let request = new Request(
      method: "GET",
      url: "http://my_url"
    );

    return this.http.request(request)
      .map(res => res.json())
      .catch(this.handleError); // Trouble line. 
                                // Without this line code works perfectly.
  

  public handleError(error: Response) 
    console.error(error);
    return Observable.throw(error.json().error || 'Server error');
  


myMethod() 在浏览器控制台中产生异常:

原始异常:TypeError:this.http.request(...).map(...).catch 不是函数

【问题讨论】:

【参考方案1】:

也许你可以尝试在你的导入中添加这个:

import 'rxjs/add/operator/catch';

你也可以这样做:

return this.http.request(request)
  .map(res => res.json())
  .subscribe(
    data => console.log(data),
    err => console.log(err),
    () => console.log('yay')
  );

每厘米:

例外:TypeError: Observable_1.Observable.throw 不是函数

同样,您可以使用:

import 'rxjs/add/observable/throw';

【讨论】:

感谢您的帮助,它有效。之后我对throw() 函数有同样的问题。我改为添加此行import 'rxjs/Rx';。现在所有运算符都可以正常工作了。 您是否模拟了错误以查看.catch 是否真的有效? .subscribe() 肯定有效。 是的,第二个问题是EXCEPTION: TypeError: Observable_1.Observable.throw is not a function。正如我上面所说的,它可以用@MattScarpino 的答案或通过这个 plunker 的方式来修复:angular.io/resources/live-examples/server-communication/ts/… 只导入 throw: import 'rxjs/add/observable/throw'; 并且不要导入所有内容,它太大了。 很好的解决方案,非常有帮助,我可能会添加 (err) 类型为 Response【参考方案2】:

新服务更新为使用 HttpClientModule 和 RxJS v5.5.x:

import  Injectable                     from '@angular/core';
import  HttpClient, HttpErrorResponse  from '@angular/common/http';
import  Observable                     from 'rxjs/Observable';
import  catchError, tap                from 'rxjs/operators';
import  SomeClassOrInterface           from './interfaces';
import 'rxjs/add/observable/throw';

@Injectable() 
export class MyService 
    url = 'http://my_url';
    constructor(private _http:HttpClient) 
    private handleError(operation: String) 
        return (err: any) => 
            let errMsg = `error in $operation() retrieving $this.url`;
            console.log(`$errMsg:`, err)
            if(err instanceof HttpErrorResponse) 
                // you could extract more info about the error if you want, e.g.:
                console.log(`status: $err.status, $err.statusText`);
                // errMsg = ...
            
            return Observable.throw(errMsg);
        
    
    // public API
    public getData() : Observable<SomeClassOrInterface> 
        // HttpClient.get() returns the body of the response as an untyped JSON object.
        // We specify the type as SomeClassOrInterfaceto get a typed result.
        return this._http.get<SomeClassOrInterface>(this.url)
            .pipe(
                tap(data => console.log('server data:', data)), 
                catchError(this.handleError('getData'))
            );
    

旧服务,使用已弃用的 HttpModule:

import Injectable              from 'angular2/core';
import Http, Response, Request from 'angular2/http';
import Observable              from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
//import 'rxjs/Rx';  // use this line if you want to be lazy, otherwise:
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';  // debug
import 'rxjs/add/operator/catch';

@Injectable()
export class MyService 
    constructor(private _http:Http) 
    private _serverError(err: any) 
        console.log('sever error:', err);  // debug
        if(err instanceof Response) 
          return Observable.throw(err.json().error || 'backend server error');
          // if you're using lite-server, use the following line
          // instead of the line above:
          //return Observable.throw(err.text() || 'backend server error');
        
        return Observable.throw(err || 'backend server error');
    
    private _request = new Request(
        method: "GET",
        // change url to "./data/data.junk" to generate an error
        url: "./data/data.json"
    );
    // public API
    public getData() 
        return this._http.request(this._request)
          // modify file data.json to contain invalid JSON to have .json() raise an error
          .map(res => res.json())  // could raise an error if invalid JSON
          .do(data => console.log('server data:', data))  // debug
          .catch(this._serverError);
    

我使用.do() (now .tap()) 进行调试。

当出现服务器错误时,我从正在使用的服务器(lite-server)获得的Response 对象的body 只包含文本,因此我在上面使用err.text() 而不是@ 987654331@。您可能需要为您的服务器调整该行。

如果res.json() 因无法解析 JSON 数据而引发错误,_serverError 将不会获得 Response 对象,这就是 instanceof 检查的原因。

在这个plunker中,把url改成./data/data.junk会产生错误。


任一服务的用户都应该有可以处理错误的代码:

@Component(
    selector: 'my-app',
    template: '<div>data</div> 
       <div>errorMsg</div>`
)
export class AppComponent 
    errorMsg: string;
    constructor(private _myService: MyService ) 
    ngOnInit() 
        this._myService.getData()
            .subscribe(
                data => this.data = data,
                err  => this.errorMsg = <any>err
            );
    

【讨论】:

【参考方案3】:

有几种方法可以做到这一点。两者都非常简单。每个例子都很好用。您可以将其复制到您的项目中并进行测试。

第一种方法更可取,第二种方法有点过时,但到目前为止它也有效。

1) 解决方案 1

// File - app.module.ts
import  BrowserModule  from '@angular/platform-browser';
import  NgModule  from '@angular/core';
import  HttpClientModule  from '@angular/common/http';

import  AppComponent  from './app.component';
import  ProductService  from './product.service';
import  ProductModule  from './product.module';

@NgModule(
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [ProductService, ProductModule],
  bootstrap: [AppComponent]
)
export class AppModule  



// File - product.service.ts
import  Injectable  from '@angular/core';
import  HttpClient  from '@angular/common/http';

// Importing rxjs
import 'rxjs/Rx';
import  Observable  from 'rxjs/Rx';
import  catchError, tap  from 'rxjs/operators'; // Important! Be sure to connect operators

// There may be your any object. For example, we will have a product object
import  ProductModule  from './product.module';

@Injectable()
export class ProductService
    // Initialize the properties.
    constructor(private http: HttpClient, private product: ProductModule)

    // If there are no errors, then the object will be returned with the product data.
    // And if there are errors, we will get into catchError and catch them.
    getProducts(): Observable<ProductModule[]>
        const url = 'YOUR URL HERE';
        return this.http.get<ProductModule[]>(url).pipe(
            tap((data: any) => 
                console.log(data);
            ),
            catchError((err) => 
                throw 'Error in source. Details: ' + err; // Use console.log(err) for detail
            )
        );
    

2) 解决方案 2。这是旧方法,但仍然有效。

// File - app.module.ts
import  BrowserModule  from '@angular/platform-browser';
import  NgModule  from '@angular/core';
import  HttpModule  from '@angular/http';

import  AppComponent  from './app.component';
import  ProductService  from './product.service';
import  ProductModule  from './product.module';

@NgModule(
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpModule
  ],
  providers: [ProductService, ProductModule],
  bootstrap: [AppComponent]
)
export class AppModule  



// File - product.service.ts
import  Injectable  from '@angular/core';
import  Http, Response  from '@angular/http';

// Importing rxjs
import 'rxjs/Rx';
import  Observable  from 'rxjs/Rx';

@Injectable()
export class ProductService
    // Initialize the properties.
    constructor(private http: Http)

    // If there are no errors, then the object will be returned with the product data.
    // And if there are errors, we will to into catch section and catch error.
    getProducts()
        const url = '';
        return this.http.get(url).map(
            (response: Response) => 
                const data = response.json();
                console.log(data);
                return data;
            
        ).catch(
            (error: Response) => 
                console.log(error);
                return Observable.throw(error);
            
        );
    

【讨论】:

【参考方案4】:

需要专门导入 RxJS 函数。一个简单的方法是使用import * as Rx from "rxjs/Rx" 导入其所有功能

然后确保以Rx.Observable 的身份访问Observable 类。

【讨论】:

Rxjs 是一个非常大的文件,如果你导入它的所有功能会增加你的加载时间 如果你只需要一两个运算符,你不应该只从 Rxjs 导入所有东西。【参考方案5】:

在最新版本的angular4中使用

import  Observable  from 'rxjs/Rx'

它将导入所有需要的东西。

【讨论】:

不要这样做,它会导入所有的 Rxjs。 因此会导致捆绑包大小增加!

以上是关于如何从 http.request() 中正确捕获异常?的主要内容,如果未能解决你的问题,请参考以下文章

如何正确赶上例外,从http.request()?

如何从 MongoDB 中正确捕获异常?

从 http.Request 获取客户端 IP 地址的正确方法

如何正确捕获 PaPal IPN 数据

如何在 golang 中为我的测试用例正确创建模拟 http.request?

如何正确捕获和处理对 Facebook api 的 Http 获取请求中的错误