如何从 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 获取客户端 IP 地址的正确方法