===============================================
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