Solvedangular HttpClient - HttpErrorResponse not json but blob

I'm submitting a...

[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Not sure if its a bug or intended behavior so can't check bug although it seems like one.

Current behavior

I am sending http request like this:

return this.http.get(url, {
      observe:'response',
      headers: headers,
      responseType:'blob'
    })

Server responds by 400 Bad Request, application/json content-type header and in body it returns json object:

{
  message: "some error message"
}

error property on HttpErrorResponse is of type Blob.

Expected behavior

I would expect HttpErrorResponse.error property to be json according to content-type server returned since documentation says:

The error property will contain either a wrapped Error object or the error response returned from the server.
ref: https://angular.io/api/common/http/HttpErrorResponse

So, if it puts server returned body into HttpErrorResponse.error then it feels like it should obey servers content-type header describing that same body.

Minimal reproduction of the problem with instructions

Use request above.

What is the motivation / use case for changing the behavior?

Environment

@angular/cli: 1.4.2
node: 6.9.3
os: win32 x64
@angular/animations: 4.4.6
@angular/common: 4.4.6
@angular/compiler: 4.4.6
@angular/core: 4.4.6
@angular/forms: 4.4.6
@angular/http: 4.4.6
@angular/platform-browser: 4.4.6
@angular/platform-browser-dynamic: 4.4.6
@angular/router: 4.4.6
@angular/cli: 1.4.2
@angular/compiler-cli: 4.4.6
@angular/language-service: 4.4.6
typescript: 2.3.4



Browser:
- [ ] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
 
For Tooling issues:
- Node version: XX  
- Platform:  

Others:
16 Answers

✔️Accepted Answer

I created this interceptor as a temporary solution until this one is fixed:

@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) {
                                console.log(e);
                                reject(err);
                            }
                        };
                        reader.onerror = (e) => {
                            console.log(e);
                            reject(err);
                        };
                        reader.readAsText(err.error);
                    });
                }
                return _throw(err);
            })
        );
    }
}

Other Answers:

Here's what I did to handle json errors with a blob response type:

  private handleError(err: HttpErrorResponse): void {
    if ('application/json' === err.headers.get('Content-Type')) {
      const reader = new FileReader();
      reader.addEventListener('loadend', (e) => {
        console.log(JSON.parse(e.srcElement['result']));
      });
      reader.readAsText(err.error);
    } else {
      console.log('not json');
    }
  }

So, if it puts server returned body into HttpErrorResponse.error then it feels like it should obey servers content-type header describing that same body.

I really like this idea. Since HttpErrorResponse.error is typed as any already, it makes sense that it would depend on the server's Content-Type for the response.

I'll look into implementing this.

JaapMosselman wrote:

I created this interceptor as a temporary solution until this one is fixed:
...

I'm posting an updated version using rxjs v6.3.3 (_throw is now throwError), plus all the needed imports.

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);
            })
        );
    }
}

More Issues: