【http/https】Content-Type的boundary
===============================================
2021/2/28_第1次修改 ccb_warlock
===============================================
写这篇文章的起因原本只是想作为一个问题的解决方案,但是经过一些测试后,发现问题并不是一个配置去掉这么简单。
一、问题与解决方案
起初我在整理java项目的接口文档时,整理到了文件上传的接口。作为刚接触java没多久的我,为了保证功能逻辑没有写错,于是调试该接口。
key(headers) | value |
Content-Type | multipart/form-data |
结果接口提示“Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found”的错误。
查了资料后了解到,使用post上传文件时,不仅需要指定mutipart/form-data来进行编码,还需要在Content-Type中定义boundary作为表单参数的分隔符。
于是加上了自定义的boundary内容:
key(headers) | value |
Content-Type | multipart/form-data; boundary=----WebKitFormBoundary7TMYhSONfkAM2z3a |
结果接口又提示“Required request part 'file' is not present”(其中file就是body中上传文件对应的key)。
由于我不会写前端,没法通过写个前端页面使用浏览器请求,从而绕过postman。而我之前用c#其实也写过类似的功能,于是我用.NET 5构建了一个上传文件的接口,再用postman请求。
首先我也是通过postman发送没有boundary的请求,结果和java一样,提供了类似的报错信息(Missing content-type boundary.)。
key(headers) | value |
Content-Type | multipart/form-data |
接着将自定义的boundary加上,再次请求,结果和java一样,提供了类似的报错信息(Unexpected end of Stream, the content may have already been read by another component.)。
key(headers) | value |
Content-Type | multipart/form-data; boundary=----WebKitFormBoundary7TMYhSONfkAM2z3a |
这样就基本排除了语言或框架的差异,确实调用存在问题。
针对这个问题,我请教了级别更高的java同事也得不到解决方案。
解决方案:
但是问题的原因并没有解决方案那么简单,实际是因为postman(5.5.5)的bug引起了这个问题,下面具体描述。
二、思考与验证
这个问题是解决了,但是又让我有了新的疑问,为什么用postman上传文件不能设置头信息content-type?
根据http标准定义,用户可以在发送上传文件请求时自定义boundary。看资料,别人对这块的理解也是用户可以自定义boundary(https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data)。
难道是老版本postman存在bug?于是下了最新的postman(8.0.6)进行测试,测试结果符合预期,即根据我定义的boundary作为分隔符。
postman(8.0.6)的抓包结果
5.5.5版本的postman由于我更新了插件后无法回退进行测试(5.5.5的桌面版在官网下不到),故我通过下了桌面客户端做了测试。
发现功能虽然正常,但是请求中boundary并不是我定义的内容,而是postman自己随机生成了字符串作为boundary。
postman(6.7.4)的抓包结果
postman(5.5.3)的抓包结果
三、总结
1)请求上传文件的接口时,需要使用post;
2)请求上传文件的接口时,需要在header信息中的Content-Type指明数据以mutipart/form-data进行编码,同时定义boundary作为分隔符(如果没有指定Content-Type,浏览器或postman会自动生成);
3)java异常中的“the request was rejected because no multipart boundary was found”、.NET中的“Missing content-type boundary.”,一般是Content-Type中没有定义boundary引起的;
4)java异常中的“Required request part 'file' is not present”(其中file就是body中上传文件对应的key)、.NET中的“Unexpected end of Stream, the content may have already been read by another component.”在我遇到的这个问题中是因为postman(5.5.5)在请求时分隔表单的分隔符使用了自动生成的字符串、而header使用了用户自定义的内容,导致接口根据头信息的boundary无法解析表单的内容。
参考资料:
1.https://blog.csdn.net/sun_977759/article/details/88868509
2.https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data
3.https://github.com/postmanlabs/postman-app-support/issues/6140