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

NestJS如何在请求体无文件时绕过FileInterceptor拦截器?

解决NestJS中SMTP邮件队列服务支持带/不带附件的问题

方案1:让FileInterceptor兼容无附件请求

直接给FileInterceptor添加fileIsRequired: false配置,同时用@Optional()标记文件参数,允许请求中不带file字段:

import { Controller, Post, UploadedFile, UseInterceptors, Optional, Body } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';

@Controller('mail')
export class MailController {
  @Post('send')
  @UseInterceptors(FileInterceptor('file', {
    fileIsRequired: false // 允许请求中没有file字段
  }))
  async sendMail(
    @Body() mailDto: MailDto,
    @Optional() @UploadedFile() file?: Express.Multer.File // 标记参数为可选
  ) {
    // 业务逻辑:存在file则添加附件,否则发送普通邮件
    const queueData = file ? { ...mailDto, attachment: file } : mailDto;
    return await this.mailQueueService.addToQueue(queueData);
  }
}

注意:这个配置需要NestJS @nestjs/platform-express 版本在8.0及以上,低版本可能不支持fileIsRequired参数。

方案2:拆分两个独立接口

如果不想调整拦截器,直接拆成两个接口分别处理:

  • 无附件邮件:不需要文件拦截器,直接接收JSON参数
  • 带附件邮件:使用FileInterceptor处理文件上传
@Controller('mail')
export class MailController {
  // 发送无附件邮件
  @Post('send')
  async sendPlainMail(@Body() mailDto: MailDto) {
    return await this.mailQueueService.addToQueue(mailDto);
  }

  // 发送带附件邮件
  @Post('send-with-attachment')
  @UseInterceptors(FileInterceptor('file'))
  async sendMailWithAttachment(
    @Body() mailDto: MailDto,
    @UploadedFile() file: Express.Multer.File
  ) {
    return await this.mailQueueService.addToQueue({ ...mailDto, attachment: file });
  }
}

方案3:自定义可选文件拦截器

如果需要统一接口,可自定义拦截器自动判断请求是否带文件:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler, mixin } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { Observable } from 'rxjs';

export function OptionalFileInterceptor(fieldName: string) {
  class Interceptor implements NestInterceptor {
    async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
      const request = context.switchToHttp().getRequest();
      // 判断请求中是否包含目标文件字段
      const hasFile = request.headers['content-type']?.includes('multipart/form-data') && 
                      request.files?.[fieldName];
      if (!hasFile) {
        return next.handle();
      }
      // 有文件时用原生FileInterceptor处理
      const fileInterceptor = new (FileInterceptor(fieldName))();
      return fileInterceptor.intercept(context, next);
    }
  }
  return mixin(Interceptor);
}

控制器中使用自定义拦截器:

@Post('send')
@UseInterceptors(OptionalFileInterceptor('file'))
async sendMail(
  @Body() mailDto: MailDto,
  @UploadedFile() file?: Express.Multer.File
) {
  // 业务逻辑同上
}

内容的提问来源于stack exchange,提问作者Châu Phước Thịnh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 18:12:11