angular: 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:

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 50
  • Comments: 15 (5 by maintainers)

Commits related to this issue

Most upvoted comments

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

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

Is there for now a simple workaround to get the json from the blob?

Is it correct that i’m still having difficulties receiving plain text using Angular 10

using (something like): const options = { responseType: ‘text’ as ‘json’, // !!! ??? headers };

return this.http.get(url, options);

I have tried several combinations of get<type> and responseType: ‘text’

(I have not tried any of the workarounds mentioned above …)

@marcosdimitrio do you have a synchronous version for this? I mean reading the Blob synchronously.

Hello,

The same problem with all responType !== 'json' whether it is text, blob or arraybuffer.

For example:

    return this.http
      .get(URL, { observe: 'response', responseType: 'arraybuffer', params })
      .pipe(
       ...

In my case I’ve a 401 Unthorized as response status, the response body will be casted to the requested responseType (an ArrayBeffer) so my RefreshToken interceptor is wating for an object (json) but got an arrayBeffer instead.

As a workaround in my interceptor:

    let clonedError = { ...error };

    if (error.error instanceof ArrayBuffer) {
      const decodedString = String.fromCharCode.apply(null, new Uint8Array(error.error));
      clonedError.error = JSON.parse(decodedString);
    }

    const newError = new HttpErrorResponse(clonedError);

which is the same workaround proposed above but for an arraybuffer, and we shoud do the same for text.

I think in case of an error, angular should only try to parse it as a json regardless of the requested responseType.

You can read blob as text and then convert it to json.