2017-09-03 43 views
5

捕獲錯誤,我有一個看起來像這樣的數據服務:在角HttpClient的

@Injectable() 
export class DataService { 
    baseUrl = 'http://localhost' 
     constructor(
     private httpClient: HttpClient) { 
    } 
    get(url, params): Promise<Object> { 

     return this.sendRequest(this.baseUrl + url, 'get', null, params) 
      .map((res) => { 
       return res as Object 
      }) 
      .toPromise(); 
    } 
    post(url, body): Promise<Object> { 
     return this.sendRequest(this.baseUrl + url, 'post', body) 
      .map((res) => { 
       return res as Object 
      }) 
      .toPromise(); 
    } 
    patch(url, body): Promise<Object> { 
     return this.sendRequest(this.baseUrl + url, 'patch', body) 
      .map((res) => { 
       return res as Object 
      }) 
      .toPromise(); 
    } 
    sendRequest(url, type, body, params = null): Observable<any> { 
     return this.httpClient[type](url, { params: params }, body) 
    } 
} 

如果我得到一個HTTP錯誤(即404),我得到一個討厭的控制檯消息: 錯誤錯誤:未捕獲( in promise):[object Object] from core.es5.js 我該如何處理它呢?

回答

19

您有一些選擇,根據您的需要。如果您想要按照每個請求處理錯誤,請將catch添加到您的請求中。如果您想添加全局解決方案,請使用HttpInterceptor

打開here the working demo plunker獲取以下解決方案。

TL;博士

在最簡單的情況下,你只需要添加一個.catch().subscribe(),如:

import 'rxjs/add/operator/catch'; // don't forget this, or you'll get a runtime error 
this.httpClient 
     .get("data-url") 
     .catch((err: HttpErrorResponse) => { 
     // simple logging, but you can do a lot more, see below 
     console.error('An error occurred:', err.error); 
     }); 

// or 
this.httpClient 
     .get("data-url") 
     .subscribe(
     data => console.log('success', data), 
     error => console.log('oops', error) 
    ); 

但也有更多的細節,這一點,見下文。


方法(本地)解決方案:登錄錯誤並返回備用響應

如果需要只在一個地方處理錯誤,你可以使用catch並返回默認值(或空響應)而不是完全失敗。您也不需要.map只需投射,您可以使用通用功能。來源:Angular.io - Getting Error Details

因此,通用.get()方法,會是這樣:

import { Injectable } from '@angular/core'; 
import { HttpClient, HttpErrorResponse } from "@angular/common/http"; 
import { Observable } from 'rxjs/Observable'; 
import 'rxjs/add/operator/catch'; 
import 'rxjs/add/observable/of'; 
import 'rxjs/add/observable/empty'; 
import 'rxjs/add/operator/retry'; // don't forget the imports 

@Injectable() 
export class DataService { 
    baseUrl = 'http://localhost'; 
    constructor(private httpClient: HttpClient) { } 

    // notice the <T>, making the method generic 
    get<T>(url, params): Observable<T> { 
     return this.httpClient 
      .get<T>(this.baseUrl + url, {params}) 
      .retry(3) // optionally add the retry 
      .catch((err: HttpErrorResponse) => { 

      if (err.error instanceof Error) { 
       // A client-side or network error occurred. Handle it accordingly. 
       console.error('An error occurred:', err.error.message); 
      } else { 
       // The backend returned an unsuccessful response code. 
       // The response body may contain clues as to what went wrong, 
       console.error(`Backend returned code ${err.status}, body was: ${err.error}`); 
      } 

      // ...optionally return a default fallback value so app can continue (pick one) 
      // which could be a default value 
      // return Observable.of<any>({my: "default value..."}); 
      // or simply an empty observable 
      return Observable.empty<T>(); 
      }); 
    } 
} 

處理錯誤可以讓你的應用程序繼續,即使在網址的服務是在惡劣的條件下。

這種按請求的解決方案主要是在您想要爲每種方法返回特定的默認響應時。但是,如果您只關心顯示錯誤(或者有全局默認響應),則更好的解決方案是使用攔截器,如下所述。

運行working demo plunker here


高級應用:攔截所有請求或響應

再次,Angular.io guide顯示:

A major feature of @angular/common/http is interception, the ability to declare interceptors which sit in between your application and the backend. When your application makes a request, interceptors transform it before sending it to the server, and the interceptors can transform the response on its way back before your application sees it. This is useful for everything from authentication to logging.

其中,當然,可以用非常簡單的方式來處理錯誤(demo plunker here):

import { Injectable } from '@angular/core'; 
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse, 
     HttpErrorResponse } from '@angular/common/http'; 
import { Observable } from 'rxjs/Observable'; 
import 'rxjs/add/operator/catch'; 
import 'rxjs/add/observable/of'; 
import 'rxjs/add/observable/empty'; 
import 'rxjs/add/operator/retry'; // don't forget the imports 

@Injectable() 
export class HttpErrorInterceptor implements HttpInterceptor { 
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { 
    return next.handle(request) 
     .catch((err: HttpErrorResponse) => { 

     if (err.error instanceof Error) { 
      // A client-side or network error occurred. Handle it accordingly. 
      console.error('An error occurred:', err.error.message); 
     } else { 
      // The backend returned an unsuccessful response code. 
      // The response body may contain clues as to what went wrong, 
      console.error(`Backend returned code ${err.status}, body was: ${err.error}`); 
     } 

     // ...optionally return a default fallback value so app can continue (pick one) 
     // which could be a default value (which has to be a HttpResponse here) 
     // return Observable.of(new HttpResponse({body: [{name: "Default value..."}]})); 
     // or simply an empty observable 
     return Observable.empty<HttpEvent<any>>(); 
     }); 
    } 
} 

提供您的攔截器:只需聲明上述HttpErrorInterceptor不會導致您的應用使用它。你需要wire it up in your app module通過提供它作爲一個攔截器,如下:

import { NgModule } from '@angular/core'; 
import { HTTP_INTERCEPTORS } from '@angular/common/http'; 
import { HttpErrorInterceptor } from './path/http-error.interceptor'; 

@NgModule({ 
    ... 
    providers: [{ 
    provide: HTTP_INTERCEPTORS, 
    useClass: HttpErrorInterceptor, 
    multi: true, 
    }], 
    ... 
}) 
export class AppModule {} 

注:如果你有錯誤攔截和一些地方的錯誤處理,自然,很可能沒有本地錯誤處理將永遠被觸發,因爲錯誤將總是由攔截器之前處理,它達到本地錯誤處理。

運行working demo plunker here

+2

好了,如果他想成爲完全看中,他將離開他的服務完全清楚:'返回this.httpClient.get (...)' 。然後在他實際使用的服務的某個地方「捕捉......」,因爲那些地方他將構建可觀察流量並且能夠最好地處理它。 –

+0

return Observable.of(e);爲我工作 這種方式我可以推遲到來電者的異常處理 – LastTribunal

+1

我同意,也許一個最佳的解決方案將是'Promise '的客戶端(DataService'方法的調用者)處理錯誤。例如:'this.dataService.post('url',{...})。then(...)。catch((e)=> console.log('handle error here',e));'' 。選擇對您和您的服務用戶更清楚的內容。 – acdcjunior

1

你可能想有這樣的事情:

this.sendRequest(...) 
.map(...) 
.catch((err) => { 
//handle your error here 
}) 

這在很大程度上取決於還你如何使用你的服務,但是這是基本的情況。

1

繼@acdcjunior答案,這是我如何實現它

服務:

get(url, params): Promise<Object> { 

      return this.sendRequest(this.baseUrl + url, 'get', null, params) 
       .map((res) => { 
        return res as Object 
       }).catch((e) => { 
        return Observable.of(e); 
       }) 
       .toPromise(); 
     } 

來電者:

this.dataService.get(baseUrl, params) 
      .then((object) => { 
       if(object['name'] === 'HttpErrorResponse') { 
          this.error = true; 
          //or any handle 
       } else { 
        this.myObj = object as MyClass 
       } 
      }); 
29

隨着HTTPClient API的到來,不僅是Http API取而代之,但新增了一個,即HttpInterceptor API。

AFAIK其目標之一是爲所有HTTP傳出請求和傳入響應添加默認行爲。

所以assumming要添加默認的錯誤處理行爲,增加.catch()您的所有可能的http.get /後的/ etc方法是可笑難以維持。

這可以通過以下方式使用來完成作爲例如HttpInterceptor

import { Injectable } from '@angular/core'; 
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http'; 
import { Observable } from 'rxjs/Observable'; 
import { _throw } from 'rxjs/observable/throw'; 
import 'rxjs/add/operator/catch'; 

/** 
* Intercepts the HTTP responses, and in case that an error/exception is thrown, handles it 
* and extract the relevant information of it. 
*/ 
@Injectable() 
export class ErrorInterceptor implements HttpInterceptor { 
    /** 
    * Intercepts an outgoing HTTP request, executes it and handles any error that could be triggered in execution. 
    * @see HttpInterceptor 
    * @param req the outgoing HTTP request 
    * @param next a HTTP request handler 
    */ 
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { 
     return next.handle(req) 
      .catch(errorResponse => { 
       let errMsg: string; 
       if (errorResponse instanceof HttpErrorResponse) { 
        const err = errorResponse.message || JSON.stringify(errorResponse.error); 
        errMsg = `${errorResponse.status} - ${errorResponse.statusText || ''} Details: ${err}`; 
       } else { 
        errMsg = errorResponse.message ? errorResponse.message : errorResponse.toString(); 
       } 
       return _throw(errMsg); 
      }); 
    } 
} 

/** 
* Provider POJO for the interceptor 
*/ 
export const ErrorInterceptorProvider = { 
    provide: HTTP_INTERCEPTORS, 
    useClass: ErrorInterceptor, 
    multi: true, 
}; 

// app.module.ts

import { ErrorInterceptorProvider } from 'somewhere/in/your/src/folder'; 

@NgModule({ 
    ... 
    providers: [ 
    ... 
    ErrorInterceptorProvider, 
    .... 
    ], 
    ... 
}) 
export class AppModule {} 

爲OP一些額外的信息:調用http.get/post/etc沒有強大的類型不是API的最佳使用。您的服務應該是這樣的:

// These interfaces could be somewhere else in your src folder, not necessarily in your service file 
export interface FooPost { 
// Define the form of the object in JSON format that your 
// expect from the backend on post 
} 

export interface FooPatch { 
// Define the form of the object in JSON format that your 
// expect from the backend on patch 
} 

export interface FooGet { 
// Define the form of the object in JSON format that your 
// expect from the backend on get 
} 

@Injectable() 
export class DataService { 
    baseUrl = 'http://localhost' 
    constructor(
     private http: HttpClient) { 
    } 

    get(url, params): Observable<FooGet> { 

     return this.http.get<FooGet>(this.baseUrl + url, params); 
    } 

    post(url, body): Observable<FooPost> { 
     return this.http.post<FooPost>(this.baseUrl + url, body); 
    } 

    patch(url, body): Observable<FooPatch> { 
     return this.http.patch<FooPatch>(this.baseUrl + url, body); 
    } 
} 

返回從您的服務方法,而不是ObservablesPromises是另一個糟糕的決定。

還有一條建議:如果您使用的是類型腳本,則開始使用類型部分。你失去了語言的最大優勢之一:瞭解你正在處理的價值的類型。

如果你想在我看來,一個角度服務的好例子,看看at the following gist

+0

評論不適合擴展討論;這個對話已經[轉移到聊天](http://chat.stackoverflow.com/rooms/153612/discussion-on-answer-by-jota-toledo-catching-errors-in-angular-httpclient)。 – deceze

+0

我假設這應該是'this.http.get()'等,而不是'this.get()'等'DataService'? – displayname

+0

所選答案似乎現在更完整。 –

-1

相當簡單(與之前的API相比)。

從Source(複製並粘貼)的Angular official guide

http 
    .get<ItemsResponse>('/api/items') 
    .subscribe(
    // Successful responses call the first callback. 
    data => {...}, 
    // Errors will call this callback instead: 
    err => { 
     console.log('Something went wrong!'); 
    } 
);