How to get body from HttpErrorResponse in Angular 6?

后端 未结 5 811
予麋鹿
予麋鹿 2020-12-11 17:14

I have created a REST API call in my Angular app which downloads a file.

I am setting responseType to \'blob\' since I am expecting a file in response.

But w

相关标签:
5条回答
  • 2020-12-11 17:32

    If the returned ContentType are different then you can leverage it to distinguish whether it's a correct binary file or a text in binary format.

    lets consider you have two files, a service, which handles your request and a component which does the business logic

    Inside your service, have your download method like:

     public downloadFile(yourParams): Observable<yourType | Blob> {
            return this._http.post(yourRequestURL, yourParams.body, {responseType: 'blob'}).pipe(
                switchMap((data: Blob) => {
                    if (data.type == <ResponseType> 'application/octet-stream') {
                        // this is a correct binary data, great return as it is
                        return of(data);
                    } else {
                        // this is some error message, returned as a blob
                        let reader = new FileReader();
                        reader.readAsBinaryString(data);  // read that message
                        return fromEvent(reader, 'loadend').pipe(
                            map(() => {
                                return JSON.parse(reader.result); // parse it as it's a text.
                                // considering you are handling JSON data in your app, if not then return as it is
                            })
                        );
                    }
                })
            );
    }
    

    In your component

     public downloadFile(params): void {
            this._service.downloadFile(params)
                subscribe((data: yourType | Blob) => {
                    if (data instanceof Blob) {
                        fileSaverSave(data, filename);  // use fileSaver or however you are downloading the content
                        // add an import for saveAs (import { saveAs as fileSaverSave } from 'file-saver';)
                    } else {
                        // do your componnet logic to show the errors
                    }
                })    
        }
    

    If you wish, you can have everything inside your component itself.

    0 讨论(0)
  • 2020-12-11 17:32

    You could try a separate error handler function, which returns the response as T as follows -

    public handleError<T>(operation = 'operation', result?: T) {
        return (error: any): Observable<T> => {
    
            // TODO: send the error to remote logging infrastructure
            console.error(error); // log to console instead
    
            // TODO: better job of transforming error for user consumption
            console.log(`${operation} failed: ${error.message}`);
    
            // Let the app keep running by returning an empty result.
            return of(result as T);
        };
    }
    

    Then simply use it to track errors in your request as follows -

    return this.http.post(this.appconstants.downloadUrl, data, { responseType: 'blob' }).pipe(
        map(this.loggerService.extractFiles),
        catchError(this.loggerService.handleError<any>('downloadFile'))    // <----
    );
    

    FYI, the function extractFiles that I used above to return a file is as follows -

    public extractFiles(res: Blob): Blob{
        return res;
    }
    
    0 讨论(0)
  • 2020-12-11 17:34

    Parameter: { observe: 'response' }, let you read the full response including the headers. See the below description:-

    Tell HttpClient that you want the full response with the observe option:

    getConfigResponse(): Observable<HttpResponse<Config>> {
        return this.http.get<Config>(this.configUrl, { observe: 'response' });
    }
    

    Now HttpClient.get() returns an Observable of typed HttpResponse rather than just the JSON data.

    this.configService.getConfigResponse()
        // resp is of type `HttpResponse<Config>`
        .subscribe(resp => {
            // display its headers
            const keys = resp.headers.keys();
            this.headers = keys.map(key =>
                `${key}: ${resp.headers.get(key)}`);
    
            // access the body directly, which is typed as `Config`.
            this.config = { ...resp.body };
        });
    

    and getting Error body like that:-

    private handleError(error: HttpErrorResponse) {
      if (error.error instanceof ErrorEvent) {
        // A client-side or network error occurred. Handle it accordingly.
        console.error('An error occurred:', error.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 ${error.status}, ` +
          `body was: ${error.error}`);
      }
      // return an observable with a user-facing error message
      return throwError(
        'Something bad happened; please try again later.');
    };
    

    import { catchError} from 'rxjs/operators';

    getConfig() { return this.http.get<Config>(this.configUrl) .pipe( catchError(this.handleError) ); }

    Reference: https://angular.io/guide/http : Reading the full response

    Change your code accordingly.

    0 讨论(0)
  • 2020-12-11 17:41

    For future visitors (since the title is generic):

    If the backend returns JSON upon error (ideally, following RFC 7807, which would also mean application/problem+json content-type too), the error.error is a JSON object, not a string. So to print it, for example, you would need to stringify it first:

    console.error(
      `Backend returned code ${error.status}, ` +
      `body was: ${JSON.stringify(error.error)}`);
    

    I believe the confusion starts from the official Angular documentation, which contains this statement:

    // The backend returned an unsuccessful response code.
    // The response body may contain clues as to what went wrong,
    console.error(
      `Backend returned code ${error.status}, ` +
      `body was: ${error.error}`);
    

    But with error.error being a JSON object (in the standard cases), you get printed [object Object] for the body instead of the string representation of that JSON object. Same unhelpful output if you try ${error.error.toString()}.

    0 讨论(0)
  • 2020-12-11 17:42

    Try this

    if(error.error instanceof Blob) {
        error.error.text().then(text => {
          let error_msg = (JSON.parse(text).message);
          console.log(error_msg)
        });
    } else {
        //handle regular json error - useful if you are offline
    }       
    
    0 讨论(0)
提交回复
热议问题