Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle error for response Type blob in HttpRequest

I am calling an http request using httpClient and using response Type as 'blob' but the problem is when it goes in error block the response type remains 'blob'.This is causing problem with my error handling.

this.httpClient.get('http://m502126:3000/reports/perdate', {
observe: 'body',
responseType: 'blob',
params: new HttpParams().set('START_DATE', startDate)
 .set('END_DATE', endDate)
 .set('MIXER', mixer)
 .set('ATTACH', 'true')

 }).subscribe(data => {
 console.log(data);
},
error => {
 console.log(error);

}

)

the problem is i am setting request type as'blob' and type of error is any . So when error comes and goes in error block the response type remains 'blob'. How to handle this ?

like image 279
vertika Avatar asked Jan 29 '18 12:01

vertika


3 Answers

I was facing the same issue. In order to handle error response from a blob request you have to parse your error content via FileReader

This is a known Angular Issue and further details can be read there. You can find different solutions for your problem there as well.

For Example you can use this function to parse your error in JSON:

  parseErrorBlob(err: HttpErrorResponse): Observable<any> {
    const reader: FileReader = new FileReader();

    const obs = Observable.create((observer: any) => {
      reader.onloadend = (e) => {
        observer.error(JSON.parse(reader.result));
        observer.complete();
      }
    });
    reader.readAsText(err.error);
    return obs;
}

and use it like this:

public fetchBlob(): Observable<Blob> {
  return this.http.get(
    'my/url/to/ressource',
    {responseType: 'blob'}
  ).pipe(catchError(this.parseErrorBlob))
}  
like image 162
SplitterAlex Avatar answered Nov 01 '22 21:11

SplitterAlex


It can also be done with: error.text()

this.dataService
  .getFile()
  .subscribe((response) => {
    FileSaver.saveAs(response.body, 'file.txt');
  }, async (error) => {
    const message = JSON.parse(await error.error.text()).message;

    this.toast.error(message, 'Error');
  });
like image 42
Marian Avatar answered Nov 01 '22 22:11

Marian


The answer by SplitterAlex mentions the Angular issue but doesn't mention a very nice solution provided there by JaapMosselman that involves creating an HttpInterceptor that will translate the Blob back to JSON.

This way, you don't have to implement this throughout your application, and when the issue is fixed, you can simply remove it.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class BlobErrorHttpInterceptor implements HttpInterceptor {
    public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(req).pipe(
            catchError(err => {
                if (err instanceof HttpErrorResponse && err.error instanceof Blob && err.error.type === "application/json") {
                    // https://github.com/angular/angular/issues/19888
                    // When request of type Blob, the error is also in Blob instead of object of the json data
                    return new Promise<any>((resolve, reject) => {
                        let reader = new FileReader();
                        reader.onload = (e: Event) => {
                            try {
                                const errmsg = JSON.parse((<any>e.target).result);
                                reject(new HttpErrorResponse({
                                    error: errmsg,
                                    headers: err.headers,
                                    status: err.status,
                                    statusText: err.statusText,
                                    url: err.url
                                }));
                            } catch (e) {
                                reject(err);
                            }
                        };
                        reader.onerror = (e) => {
                            reject(err);
                        };
                        reader.readAsText(err.error);
                    });
                }
                return throwError(err);
            })
        );
    }
}

Declare it in your AppModule or CoreModule:

import { HTTP_INTERCEPTORS } from '@angular/common/http';
...

@NgModule({
    ...
    providers: [
        {
            provide: HTTP_INTERCEPTORS,
            useClass: BlobErrorHttpInterceptor,
            multi: true
        },
    ],
    ...
export class CoreModule { }
like image 18
Marcos Dimitrio Avatar answered Nov 01 '22 22:11

Marcos Dimitrio