2017年9月30日 星期六

[jquery]jquery ajax post能夠正確Model Bind到Asp .net Mvc網站 - 三種不同情境的建議做法

image
圖片來源:https://pixabay.com/en/key-tag-security-label-symbol-2114047/

Model Binding是Asp .net Mvc裡面用來處理表單送出(Form Post)資料自動轉成強型別的機制。

一般來說,如果用HtmlHelper產生的html內容,在Post back的時候Model Binding基本上不會遇到什麼問題。

不過為了網站responsive更好,很多時候會希望Form Post是透過ajax來做。

這個時候依據不同做法,就很容易造成form post到asp .net mvc的時候Model Binding不到。

這篇將會建議不同情境的時候應該如何寫正確的Jquery Form post寫法,避免Model Binding不到的問題。

這篇文章相關的範例程式碼在:https://github.com/alantsai-samples/aspnetmvc-jquery-post-model-binding-sample,有用tag標註每一個不同的情境。

三種情境建議做法 - TL;DR

基本上要form post的時候會遇到三種不同的情況:

  1. 想Post的內容是所有的Form欄位 - 換句話說只是要ajax post模擬一個submit的效果
  2. 想post的內容只是表單裡面幾個欄位,並非全部的欄位
  3. 想Post的內容包含檔案上傳

針對這三個不同情境,有幾個不同的jquery post方式建議:

想Post的內容是所有的Form欄位

只需要post的時候,data欄位傳入:$("#postForm").serialize() 即可。

片段範例:

$.ajax({
 type: "POST",
 url: "@Url.Action("JqueryPost")",
 data: $("#postForm").serialize()
})
想post的內容只是表單裡面幾個欄位
  1. 組出最後要送出的javascript 物件 data
  2. 在 ajax 的 data欄位值輸入:JSON.stringify(data)
  3. 在 ajax的 contentType欄位值輸入:"application/json"

片段範例:

var data = [];
data.push({ Title: $("input[name='[0].Title']").val(), Content: $("input[name='[0].Content']").val() });
data.push({ Title: $("input[name='[1].Title']").val(), Content: $("input[name='[1].Content']").val() });

$.ajax({
 type: "POST",
 url: "@Url.Action("JqueryPost")",
 data: JSON.stringify(data),
 contentType: "application/json"
})
想post的內容包含檔案上傳
可以使用FormData,注意,只有支援Html 5的browser才能夠用,所以IE 10 以下就不用考慮了。
  1. Form htmlelement 建立 FormData - 例如:var data = new FormData($("#postForm")[0]);
  2. 在 ajax 的 data 欄位值輸入第一步建立出來的data
  3. 在 ajax 的 processData欄位值輸入:false
  4. 在 ajax 的 contentType欄位值輸入:false

片段範例:

var data = new FormData($("#postForm")[0]);

$.ajax({
 type: "POST",
 url: "@Url.Action("JqueryPost")",
 data: data,
 processData: false,
 contentType: false
})

如果對於為什麼這三種情境是這樣建議有興趣,請往下看.....

三種情境做法建議原因

依照不同的情境,jquery post的呼叫方式其實也會需要不同不然到asp .net mvc model binding的時候很容易失敗,會話很多時間在debug這些問題,以下將會對於3個情境的做法進一步說明。

想Post的內容是所有的Form欄位

一般來說,在建立Form表單的時候,撰寫上會使用HtmlHelper,這種產生出來的html內容只要post上去asp .net mvc model binding基本上沒有什麼問題。

因此可以利用Form表單本身然後透過jquery序列化的方式作為送出的資料。

假設我們的Form表單的id是postForm,那麼整個post方式就是:

$.ajax({
 type: "POST",
 url: "@Url.Action("JqueryPost")",
 data: $("#postForm").serialize()
})

換句話說其實就是用jquery模擬一般的postsubmit

想post的內容只是表單裡面幾個欄位

有時候要post的內容可能不全部在同一個Form表單,或者可能有幾個不同地方的欄位組成,這個時候,第一種情境的序列化做法就不適合了。

這個時候可能會想說建立一個javascript 物件,然後直接post這個物件。例如,假設後端要binding的model 有 Title和Content欄位:

var data = {};
data.Title = "a";
data.Content = "b";

$.ajax({
 type: "POST",
 url: "@Url.Action("JqueryPost")",
 data: data
})

這個會binding成功,因此可能想說都是這麼處理就好,但是,當要binding的物件比較複雜的時候,這個方式就會binding失敗

舉例來說,假設我們有同樣的model結構,但是這個時候後台變成需要的是一個list,這個時候可能想說傳入array即可:

var data = [];
data.push({ Title: "1", Content: "2" });
data.push({ Title: "2", Content: "4" });

$.ajax({
 type: "POST",
 url: "@Url.Action("JqueryPost")",
 data: data
})

但是這個會binding失敗,主要原因是因為預設jquery使用的是application/x-www-form-urlencoded作為ContentType - 這種方式的產生結構類似於querystring的那種串聯方式,因此在處理複雜結構 的時候產生出來的內容不符合asp .net mvc預設model binding的邏輯。

因此建議直接傳送json格式的內容,在處理複雜結構沒有什麼問題。

不過要使用json格式的資料就會需要:

  • JSON.stringfy()把物件轉成json的字串樣子
  • ContentType設定成為:application/json - 告知mvc傳入的內容是json

因此整個範例變成:

var data = [];
data.push({ Title: "1", Content: "2" });
data.push({ Title: "2", Content: "4" });

$.ajax({
 type: "POST",
 url: "@Url.Action("JqueryPost")",
 data: SON.stringify(data),
 contentType: "application/json"
})

想post的內容包含檔案上傳

當想要post上去的內容包含檔案上傳(input type="file")的時候,上述提到的2種做法都沒有辦法達成。

原因是因為檔案上傳要包含的資訊豐富很多,上述兩種方式都沒有辦法達到。

因此當需要post的內容包含檔案上傳,需要使用特殊的資料類型FormData

FormData只有在支援Html 5的browser才有支援,因此IE 10 以下沒有辦法。

要使用FormData非常的簡單,只需要在new的時候傳入Form的html element物件即可。

有了FormData作為資料之後,還有兩個設定:

  1. processData要設定為false避免jquery傳送的時候有額外做一些處理
  2. ContentType設定為false讓jquery決定送出的類型

整個的範例如下:

var data = new FormData($("#postForm")[0]);

$.ajax({
 type: "POST",
 url: "@Url.Action("JqueryPost")",
 data: data,
 processData: false,
 contentType: false
})

結語

越來越多網站會要求直接使用ajax - 整個畫面不需要post給使用者感官沒有load那麼久。

希望透過這篇能夠提供一個Reference,當要用jquery ajax post的時候,asp .net mvc model binding不到的時候可以參考以下,避免在抓頭了。


沒有留言 :

張貼留言