Angular POST multipart/form-data请求无multipart边界报错排查
Angular提交FormData请求提示无multipart边界问题排查
问题现象
- 项目基于Angular框架开发,通过
HttpClient提交FormData格式的带附件请求,全局HttpInterceptor会为匹配接口统一追加自定义请求头 - 相同参数通过Postman调用接口可正常返回,Angular侧发起请求时服务端抛出错误:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found - 尝试手动移除请求中的
Content-Type头后,又触发unsupported media type错误,无法定位问题根因
关联代码
HttpInterceptor实现
if (request.url.includes(baseUrl)) { request = request.clone({ headers: new HttpHeaders({ 'Request-Date': currentDate.toString(), 'Request-Id': requestId, "Accept": "application/json" , param1: environment.encriptionEnabled ? requestData['param1'] : 'null', locale: 'en', }), body: environment.encriptionEnabled ? requestData.ciphertext : request.body }); }
后端接口定义
@PostMapping(value = RequestURIConstant.SEND_EMAIL_WITH_ATTACHMENT, consumes = { MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE }) public void sendEmailWithAttachment(@RequestPart("request") String request, @RequestPart("file") MultipartFile file) { EmailNotificationRequest emailRequest = externalNotification.getJson(request); LOG.info("Request received to send email"); externalNotification.sendMail(emailRequest, file); }
报错截图



根因与修复方案
- 核心问题出在拦截器的请求头覆写逻辑:使用
new HttpHeaders()传入固定配置对象时,会完全清空请求原有头再写入新配置。Angular的HttpClient在识别到请求体是FormData类型时,会自动生成带随机boundary参数的Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxxx头,这个boundary是服务端拆分解析multipart请求体的唯一标识,被覆写逻辑覆盖后服务端找不到边界标识,自然抛出对应错误。 - 手动删除
Content-Type后触发媒体类型不支持错误,是因为直接移除该头后服务端无法识别请求体格式,匹配不到接口的consumes配置。 - 修复逻辑:不要直接实例化全新的
HttpHeaders对象替换原有头,在原有请求头的基础上追加自定义头,保留Angular自动生成的Content-Type配置;同时针对FormData类型请求跳过加密逻辑的body替换,避免破坏FormData结构:
if (request.url.includes(baseUrl)) { const isFormDataReq = request.body instanceof FormData; // 在原有请求头基础上追加自定义头,不覆盖自动生成的Content-Type const updatedHeaders = request.headers .set('Request-Date', currentDate.toString()) .set('Request-Id', requestId) .set('Accept', 'application/json') .set('param1', environment.encriptionEnabled ? requestData['param1'] : 'null') .set('locale', 'en'); // FormData请求不替换body,避免破坏 multipart 结构 const updatedBody = environment.encriptionEnabled && !isFormDataReq ? requestData.ciphertext : request.body; request = request.clone({ headers: updatedHeaders, body: updatedBody }); }
- 注意:业务代码发起FormData请求时,禁止手动在请求配置中指定
Content-Type,完全交给HttpClient自动生成即可。手动写入的头不会携带和请求体匹配的boundary参数,依然会触发服务端解析错误。 - 验证方法:修改完成后打开浏览器网络面板,查看对应请求的
Content-Type头是否携带自动生成的boundary参数,确认后发起请求即可正常解析。
内容的提问来源于stack exchange,提问作者Amit
相关产品推荐
相关产品推荐

