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

NestJS Axios拦截器处理文件上传请求时Token刷新后请求无限挂起的问题排查

NestJS Axios拦截器处理文件上传请求时Token刷新后请求无限挂起的问题排查

嘿,我仔细看了你的问题和代码,这个无限挂起的坑其实挺常见的——核心问题出在FormData里的文件流是一次性的,咱们一步步拆解解决:

问题根源分析

当你用fs.createReadStream()创建文件流并塞进FormData时,这个流只能被读取一次。第一次请求因为Token过期失败后,拦截器直接复用了原来的originalRequest,但此时FormData里的文件流已经被消费空了,Axios一直在等流返回数据,自然就无限卡住了。

另外还有个小细节:你的拦截器类声明了要实现NestInterceptor接口,但没写intercept方法,不过这个是次要问题,先解决流的复用才是关键。

具体解决方案

咱们需要在重试时重新生成FormData和文件流,具体分两步改:

1. 给文件上传请求添加可复用的FormData生成逻辑

在发起上传请求时,不要直接把FormData实例传给Axios,而是存一个生成FormData的函数,这样重试时能重新生成新的实例和流:

async uploadAttachmentToDummyEntry(DummyEntryId: number, attachmentFilepath: string) {
  this.logger.log("Uploading attachment to Dummy Entry started...");
  
  // 定义生成FormData的函数,每次调用都会创建新的流
  const createFormData = () => {
    let uploadAttachmentFormData = new FormData();
    uploadAttachmentFormData.append("attachment[file]", fs.createReadStream(attachmentFilepath));
    return uploadAttachmentFormData;
  };

  const config = {
    maxBodyLength: 100 * 1024 * 1024, // 100 MB in bytes
    maxContentLength: 100 * 1024 * 1024, // 100 MB in bytes
    headers: {
      "Accept": "application/json",
      // 别手动写Content-Type!FormData会自动生成带boundary的正确值
    },
    // 把生成函数存到自定义字段里,方便拦截器取用
    _formDataCreator: createFormData
  };

  // 第一次请求时生成FormData,并自动获取正确的headers
  const formData = createFormData();
  config.headers = { ...config.headers, ...formData.getHeaders() };

  const asd = await firstValueFrom(
    this.http.post(
      `${this.configService.get("Dummy_API_URL")}/invoices/${DummyEntryId}/attachments`,
      formData,
      config
    )
  ).catch((error) => {
    console.log(error);
    return error;
  });

  return asd;
}

2. 修改拦截器的重试逻辑,重新生成请求体

在拦截器的错误处理里,判断如果是文件上传请求(有_formDataCreator字段),就重新生成FormData和headers再重试:

@Injectable()
export class DummyInterceptor implements NestInterceptor { // 必须实现接口才能生效
  private readonly logger = new Logger(DummyInterceptor.name);
  DummyApiBearerToken: string;

  constructor(
    private httpService: HttpService, 
    private DummyService: DummyService, 
    private configService: ConfigService
  ) {
    this.httpService.axiosRef.interceptors.request.use((config) => {
      console.log("Axios request interceptor");
      if (config.url.startsWith(this.configService.get("Dummy_API_URL"))) {
        config.headers["Authorization"] = "Bearer " + this.DummyApiBearerToken;
        config.headers["Accept"] = "application/json";
        console.log(config.headers);
      }
      return config;
    });

    this.httpService.axiosRef.interceptors.response.use((response) => {
      console.log("Axios response interceptor");
      console.log(response.data);
      return response;
    }, async (error) => {
      this.logger.log("Dummy API error interceptor");
      if (error.response?.data) {
        this.logger.error(error.response.data);
      }
      const originalRequest = error.config;

      if (error.response?.status === HttpStatusCode.Unauthorized && !originalRequest._retry) {
        this.logger.log("Unauth, refreshing Dummy bearer");
        originalRequest._retry = true;

        const response = await this.DummyService.getNewBearerToken();
        this.DummyApiBearerToken = response.data["access_token"];

        // 处理文件上传的重试:重新生成FormData和headers
        if (originalRequest._formDataCreator) {
          const newFormData = originalRequest._formDataCreator();
          originalRequest.data = newFormData;
          originalRequest.headers = { ...originalRequest.headers, ...newFormData.getHeaders() };
        }

        return this.httpService.axiosRef(originalRequest);
      }
      return Promise.reject(error);
    });
  }

  // 实现intercept方法,Nest才能识别这是个拦截器
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle();
  }
}

额外提醒

  • 绝对不要手动设置multipart/form-data的Content-Type,FormData会自动生成带boundary的正确值,手动写会导致后端解析失败。
  • 确保拦截器正确实现了NestInterceptor的intercept方法,否则Nest不会加载这个拦截器。

这样修改后,Token过期重试时会重新创建文件流和FormData,请求就能正常发送完成,不会再无限挂起了。

备注:内容来源于stack exchange,提问作者Dev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 15:48:01