You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何API返回的错误响应与Angular捕获的不一致?如何修复?

文件大小超限API异常在Angular前端无法正确捕获的问题

问题背景

我有一个文件下载API,当待下载文件大小超过设定最大值时会抛出异常,后端JAX-RS代码如下:

@GET
@Path("files")
public Response getFile(@QueryParam("filePath") String filePath) throws IOException {
    File file = new File(filePath);

    long fileSizeKb = Math.max(file.length() / 1024, 1);

    if (fileSizeKb > APIServerProperties.MAX_DOWNLOADABLE_FILE_SIZE_KB) {
        String msg = MessageBundle.getMessage(MessageKeys.ERR_FILE_SIZE_EXCEEDED, String.valueOf(fileSizeKb),
                String.valueOf(APIServerProperties.MAX_DOWNLOADABLE_FILE_SIZE_KB));
        System.out.println("msg: " + msg);
        throw new GeneralException(msg,
                new FileSizeLimitExceededException(msg, fileSizeKb, APIServerProperties.MAX_DOWNLOADABLE_FILE_SIZE_KB));
    }

    // Read file content into byte array
    byte[] fileBytes = readFileContent(file);

    return Response.ok(fileBytes).header("Content-Disposition", "attachment; filename=" + file.getName()).build();
}

该API由以下Angular组件函数调用:

downloadFile(filePath: string) {
    this.migrationService.getFile(filePath).subscribe({
      next: (response) => {
        const fileContent: Uint8Array = new Uint8Array(response);
        const fileContentStr = new TextDecoder().decode(fileContent);

        const forwardSplit = filePath.split("/");
        const backwardSplit = forwardSplit[forwardSplit.length - 1].split("\\");
        let fileName = backwardSplit[backwardSplit.length - 1];

        const element = document.createElement("a");
        element.setAttribute(
          "href",
          "data:text/cvs;charset=utf-8," + encodeURIComponent(fileContentStr)
        );
        element.setAttribute("download", fileName);
        element.style.display = "none";
        document.body.appendChild(element);
        element.click();
        document.body.removeChild(element);
      },
      error: (err) => {
        console.log("err", err);
      },
    });
  }

触发错误时,API返回了预期的错误响应,但在downloadFile函数的控制台日志中,捕获到的错误却与该响应不一致,无法获取所需的响应对象。请问原因是什么以及如何修复?


原因分析

  1. 响应类型不匹配:API成功时返回二进制byte[],但后端抛出异常时,JAX-RS默认返回的错误响应是JSON或文本格式。Angular的HttpClient如果请求时指定了responseType为arraybuffer(适配二进制文件),错误响应也会被按二进制解析,导致无法直接读取错误信息。
  2. 错误解析逻辑缺失:前端错误处理函数未对二进制格式的错误响应进行解码,直接打印err只能看到原始的ArrayBuffer对象,无法提取后端返回的错误内容。

修复方案

步骤1:统一后端错误响应格式(推荐)

通过自定义ExceptionMapper,将异常转换为标准JSON格式的错误响应,确保前端能稳定解析:

@Provider
public class GeneralExceptionMapper implements ExceptionMapper<GeneralException> {
    @Override
    public Response toResponse(GeneralException exception) {
        // 自定义错误响应POJO
        class ErrorResponse {
            private String message;
            private long fileSizeKb;
            private long maxAllowedSizeKb;

            public ErrorResponse(String message, long fileSizeKb, long maxAllowedSizeKb) {
                this.message = message;
                this.fileSizeKb = fileSizeKb;
                this.maxAllowedSizeKb = maxAllowedSizeKb;
            }

            // 省略getter方法
        }

        FileSizeLimitExceededException cause = (FileSizeLimitExceededException) exception.getCause();
        ErrorResponse errorResponse = new ErrorResponse(
                exception.getMessage(),
                cause.getFileSize(),
                cause.getMaxSize()
        );

        return Response.status(Response.Status.REQUEST_ENTITY_TOO_LARGE)
                .entity(errorResponse)
                .type(MediaType.APPLICATION_JSON)
                .build();
    }
}

步骤2:调整Angular请求与错误处理

服务层修改:指定响应类型并获取完整响应

// migrationService中的getFile方法
getFile(filePath: string): Observable<HttpResponse<ArrayBuffer>> {
  return this.http.get<ArrayBuffer>(`/api/files`, {
    params: { filePath: encodeURIComponent(filePath) },
    responseType: 'arraybuffer' as 'json',
    observe: 'response' // 获取完整HTTP响应对象,包含状态码、头信息
  });
}

组件层修改:正确解析错误响应

downloadFile(filePath: string) {
  this.migrationService.getFile(filePath).subscribe({
    next: (response) => {
      const fileContent: Uint8Array = new Uint8Array(response.body);
      const fileContentStr = new TextDecoder().decode(fileContent);

      // 解析文件名逻辑保持不变,修复拼写错误:text/cvs → text/csv
      const forwardSplit = filePath.split("/");
      const backwardSplit = forwardSplit[forwardSplit.length - 1].split("\\");
      const fileName = backwardSplit[backwardSplit.length - 1];

      const element = document.createElement("a");
      element.setAttribute(
        "href",
        "data:text/csv;charset=utf-8," + encodeURIComponent(fileContentStr)
      );
      element.setAttribute("download", fileName);
      element.style.display = "none";
      document.body.appendChild(element);
      element.click();
      document.body.removeChild(element);
    },
    error: (err) => {
      // 处理二进制格式的错误响应
      if (err.error instanceof ArrayBuffer) {
        const errorText = new TextDecoder().decode(err.error);
        try {
          const errorObj = JSON.parse(errorText);
          console.log("错误详情:", errorObj);
          // 可将错误信息展示给用户,比如弹窗
          alert(errorObj.message);
        } catch (e) {
          // 后端返回纯文本错误时直接展示
          console.log("错误信息:", errorText);
          alert(errorText);
        }
      } else {
        // 处理其他类型错误
        console.log("请求错误:", err);
        alert("文件下载失败,请稍后重试");
      }
    },
  });
}

额外修正

前端代码中的text/cvs是拼写错误,已修正为text/csv,避免下载文件的格式识别异常。


内容的提问来源于stack exchange,提问作者ericute

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 01:45:03