Angular 5 管理带有 blob 响应和 json 错误的 http get

Posted

技术标签:

【中文标题】Angular 5 管理带有 blob 响应和 json 错误的 http get【英文标题】:Angular 5 manage http get with blob response and json errors 【发布时间】:2018-09-03 22:03:19 【问题描述】:

我正在开发一个 Angular 5 应用程序。我必须从我的后端应用程序下载一个文件,为此我只需调用如下函数:

public executeDownload(id: string): Observable<Blob> 
  return this.http.get(this.replaceUrl('app/download', denunciaId), responseType: 'blob').map(result => 
    return result;
  );

然后调用我刚刚调用的下载服务:

public onDownload() 
  this.downloadService.executeDownload(this.id).subscribe(res => 
    saveAs(res, 'file.pdf');
  , (error) => 
    console.log('TODO', error);
    // error.error is a Blob but i need to manage it as RemoteError[]
  );

当后端应用程序处于特定状态时,它不会返回 Blob,而是返回一个 HttpErrorResponse,其中在其 error 字段中包含一个 RemoteError 数组。 RemoteError 是我编写的用于管理远程错误的接口。

在 catch 函数中,error.error 是一个 Blob。如何将 Blob 属性转换为 RemoteError[] 数组?

提前致谢。

【问题讨论】:

【参考方案1】:

使用FileReader 的建议对我来说还不够,因为它们不适用于HttpTestingController(因为blob 到json 的转换是异步的)。在我的案例中,业力测试总是在该承诺得到解决之前完成。这意味着我不能编写使用这种方法来测试不愉快路径的业力测试。我将建议一个将 blob 同步转换为 json 的解决方案。

服务类:

public doGetCall(): void 
    this.httpClient.get('/my-endpoint', observe: 'body', responseType: 'blob').subscribe(
        () => console.log('200 OK'),
        (error: HttpErrorResponse) => 
            const errorJson = JSON.parse(this.blobToString(error.error));
            ...
        );


private blobToString(blob): string 
    const url = URL.createObjectURL(blob);
    xmlRequest = new XMLHttpRequest();
    xmlRequest.open('GET', url, false);
    xmlRequest.send();
    URL.revokeObjectURL(url);
    return xmlRequest.responseText;

角度测试:

it('test error case', () => 
    const response = new Blob([JSON.stringify(error-msg: 'get call failed')]);

    myService.doGetCall();

    const req = httpTestingController.expectOne('/my-endpoint');
    expect(req.request.method).toBe('GET');
    req.flush(response, status: 500, statusText: '');
    ... // expect statements here
);

错误子句中已解析的errorJson 现在将包含error-msg: 'get call failed'

【讨论】:

【参考方案2】:

可能像大多数人一样,我希望我的错误消息同步。我通过将它放在一个警告框中来处理这个问题:

(err:any) =>  

    // Because result, including err.error, is a blob,
    // we must use FileReader to display it asynchronously:
    var reader = new FileReader();
    reader.onloadend = function(e) 
      alert("Error:\n" + (<any>e.target).result);
    
    reader.readAsText(err.error);

    let errorMessage = "Error: " + err.status.toString() + " Error will display in alert box.";
    // your code here to display error messages.
,

【讨论】:

【参考方案3】:

响应应该是一个 Blob,但显然情况并非如此。 为避免此错误,请将 responseType 从 blob 更改为 arraybuffer。

public executeDownload(id: string): Observable<Blob> 
  return this.http.get(this.replaceUrl('app/download', denunciaId), responseType: 'arraybuffer').map(result => 
    return result;
  );

【讨论】:

【参考方案4】:

这是一个已知的Angular issue,在该线程中,JaapMosselman 提供了一个非常好的解决方案,涉及创建一个 HttpInterceptor,它将 Blob 转换回 JSON。

使用这种方法,您不必在整个应用程序中进行转换,当问题得到解决后,您只需将其删除即可。

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

在你的 AppModule 或 CoreModule 中声明它:

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

@NgModule(
    ...
    providers: [
        
            provide: HTTP_INTERCEPTORS,
            useClass: BlobErrorHttpInterceptor,
            multi: true
        ,
    ],
    ...
export class CoreModule  

【讨论】:

应该是公认的答案,完美,复制/粘贴。【参考方案5】:

如文档“从 Blob 读取内容的唯一方法是使用 FileReader。” https://developer.mozilla.org/en-US/docs/Web/API/Blob.

编辑: 如果你需要blob的一部分,你可以做一个切片,它返回新的blob, 然后使用文件阅读器。

【讨论】:

以上是关于Angular 5 管理带有 blob 响应和 json 错误的 http get的主要内容,如果未能解决你的问题,请参考以下文章

Angular 6获取带有httpclient问题的响应标头

如何在 Angular 5 中解析和播放数据库中的 Blob/二进制数据

带有 Angular2 应用程序和 NodeJs 的 Docker 容器没有响应

如何在 Angular 中从服务器获取带有 jwt 令牌的响应标头

下载文件时在文件内容中获取类型 [object object],而不是使用 angular 7 获取 blob

带有 angularjs 的 Internet Explorer 中的 Blob url