利用ModelBinding處理FormData回傳表單「多值集合」與「多重檔案」之作法
今天遇到使用Model Binding來快速處理表單AJAX回傳FormData時,表單中需要包含多值集合的項目,一直以來沒有在Model Binding的模型下進行這樣的資料型態處理,因此進行個小實驗來驗證一下,另外也順便實驗了多重檔案上傳的可能性,將程式碼紀錄於此供給有需要的人查閱。
表單包含多值集合的項目
舉例來說,有一份考卷資料,裡面包含了成績單的名稱,另外包含了一個學生成績清冊列表的集合,另外也有可能存在著考卷的影像檔案,這些資料如何透過FormData以AJAX的方式向後端傳送,並讓後端可以順利的讀取呢?
後端資料模型
public class Exam
{
public string cName { get; set; }
public System.Collections.Generic.List<Student> oStudents { get; set; }
}
public class Student
{
public string cName { get; set; }
public int iScore { get; set; }
}
值得一提的是,在一些網路文獻裡面有提到,可以在Exam類別下加上HttpPostedFile或是HttpPostedFileBase型別的檔案屬性資料,但就我實際上的測試來說,並不會被Model Binding自動識別與讀取。原因是先前我的WebForm DataBinding支援能力是從微軟的原始碼幹過來的,而有關於這方面全部的實作微軟早就轉移陣地至System.Web.MVC之中,包含IValueProvider、ValueProviderFactories、HttpFileCollectionValueProviderFactory HttpFileCollectionValueProvider...等實作,這麼龐大的架構移植到現行的WebForm框架中根本不符成本,因此直接放棄改成由Request.Files(HttpFileCollection)來拿即可。
說白點就是有關「HTTP檔案上傳」這部分放棄由ModelBinding來完成。
前端表單HTML
<form id="myForm">
<fieldset>
<div class="form-row">
<div class="form-group col-12">
<label>考卷名稱</label>
<div class="input-group">
<input type="text" class="form-control" id="cName" name="cName">
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-12 col-md-6">
<label>學生A姓名</label>
<div class="input-group">
<input type="text" class="form-control" id="oStudents[0].cName" name="oStudents[0].cName">
</div>
</div>
<div class="form-group col-12 col-md-6">
<label>學生A分數</label>
<input type="text" class="form-control" id="oStudents[0].iScore" name="oStudents[0].iScore">
</div>
</div>
<div class="form-row">
<div class="form-group col-12 col-md-6">
<label>學生B姓名</label>
<div class="input-group">
<input type="text" class="form-control" id="oStudents[1].cName" name="oStudents[1].cName">
</div>
</div>
<div class="form-group col-12 col-md-6">
<label for="iMoney">學生B分數</label>
<input type="text" class="form-control" id="oStudents[1].iScore" name="oStudents[1].iScore">
</div>
</div>
<div class="form-row">
<div class="form-group col-12 col-md-6">
<label>考卷影像檔1</label>
<div class="input-group">
<input type="file" id="oPaperFile1" name="oPaperFile1">
</div>
</div>
<div class="form-group col-12 col-md-6">
<label>考卷影像檔2</label>
<div class="input-group">
<input type="file" id="oPaperFile2" name="oPaperFile2">
</div>
</div>
</div>
</fieldset>
</form>
前端表單Javascript
$.ajax({
url: cUrl,
type: "POST",
data: new FormData($("#myForm")[0]),
processData: false, //FormDataRequired
contentType: false, //FormDataRequired
dataType: "json",
beforeSend: function () {
//傳輸中
},
complete: function () {
//傳輸完成
},
error: function (oXHR, cStatus, cError) {
//傳輸錯誤
},
success: function (oData) {
//傳輸成功
}
});
觀察一下傳輸過去的資料:
可以藉機看到multipart/form-data的依據,全部都是表單中的name屬性而已,所以在HTML中id屬性就算拿掉也可以正常運作。而「多值集合」與「多重檔案」的傳輸秘訣,關鍵就在於name屬性以陣列[n]的方式來描述即可。
後端讀取模型
//WebForm ModelBinding
var oMB = WebFormModelBinding.ModelBinding<Exam>();
//取得試卷名稱
oMB.oData.cName;
//取得考生姓名與成績
oMB.oData.oStudents.Select(x => $"{x.cName}|{x.iScore}");
//取得前端AJAX回傳的檔案並儲存
for (int i = 0; i < Request.Files.Count; i++)
{
var oFile = Request.Files[i];
oFile.SaveAs($@"\\YourFileServer\" + oFile.FileName);
}
Slashview.ModelBinding這個方法,可以在相關參考中找到之前的文章。
相關參考
- 進行物件Reflection反射後,將值Value寫入到物件屬性中
- 在WebForm下使用ModelBinding(PostBack遭遇之問題)
- 在WebForm下使用ModelBinding(打造ASHX可用的通用類別)
- 利用ModelBinding處理FormData回傳表單「多值集合」與「多重檔案」之作法