在上一篇文章(如何在 .NET Core WebApi 中處理 MultipartFormDataContent)中,我們有描述過如何以最簡單的方式在 .NET Core WebApi 中處理 MultipartFormDataContent 。基於框架層面的封裝,我們可以快速的從 Request.Form 中分別拿到檔案內容和文字內容,但是這些預設的解析方式都是建立在前後端以標準的資料格式來進行構建和解析。
問題描述
上圖示例展示了使用者透過 IOS 客戶端傳送請求時,對應後端介面接收到的 Request 內容。從請求內容的整體結果,我們可以看出這是一個 multipart/form-data 的資料格式,由於這種資料是由多個 multipart section 組成,所以我們可以看出在這個請求體中,是包含3個 section ,name 值分別為 Agree,CultureCode,FingerSignature,每個 section 都會包含一個 Content-Disposition 欄位,前面兩個 section都是普通的資料格式,最後一個是圖片型別的資料。當後端介面接收到這樣一個請求體時,嘗試使用 Request.Form.Files 的方式來獲取目標檔案時,發現無法獲取 FingerSignature 對應的檔案內容。
問題分析
透過和客戶溝通,瞭解到FingerSignature 對應的檔案是會被一起放到請求體中傳給後端,客戶表示前端APP這一塊的邏輯在後端還沒有升級成 ASP.NET Core(處於 Framework 階段)的時候是可以正常工作的。透過檢視ASP.NET Core 中對 Request.Form 的賦值邏輯: FormFeature 實現,找到了如下邏輯函式:
透過原始碼的邏輯,我們可以看出,只有當前的 section 對應的 Content-Disposition 同時包含 form-data 和 fileName (或 fileNameStar),才會被作為檔案來處理(示例:form-data; name="FingerSignature"; fileName=”xxxx.jpeg”),否則並不會把當起的 Section 新增到 Request.Form.Files 中。此時結合上面獲取的請求內容,定位到 FingerSignature 部分的 Content-Disposition 中由於缺少 fileName 欄位導致後端無法解析到對應檔案為該issue 的 Root Cause。
解決方案
由於前端 APP 已經發布多個版本,所以讓前端來補全這個欄位顯然不是一種穩妥的修復方案,因此後端需要做一個相容性處理。當遇到這種不是標準格式的檔案內容,需要透過 MultipartReader 物件來處理 MultipartFormDataContent 對應的 Section 內容(實際上 Request.Form.Files 底層邏輯就是透過 MultipartReader 來依次解析每個 Section 內容)。這裡實現了一個方法來獲取當前 MultipartFormDataContent 中的所有檔案:
在這個方法中有2個細節地方需要注意。
- 預設情況下,Request.Body 中的內容只允許讀取一次,所以我們需要在使用這個方法的路由地方啟用 EnableBuffering 設定,這裡可以自定義一個 Filter 來複用這種特性:
- 因為對 Request.Body開啟 EnableBuffering 了,所以在呼叫 ReadFilesAsyns 方法的時候,不確定此時 Body 中的 Stream起始位置為 0。所以我們需要在讀取 Body 之前和之後透過 Seek 方法將 Stream的 Position 重置歸 0。
最佳化建議
從目前的修復方案來講的話,後端只是提供了一種妥協的修復方案來適配前端的資料不完整,因此感覺比較完善的修復方案是前端在傳送 multipart/form-data 的資料時,儘量以標準的方式來構建每個 Section 內容,尤其是檔案型別。如果前後端都能以統一的資料格式來進行互動,自然也就不會出現上述所說的這種問題,潛在的風險自然也就變小了。