Asp.Net MVC 使用 Ajax
Ajax
簡單來說Ajax是一個無需重新加載整個網頁的情況下,可以更新局部頁面或數據的技術(異步的發送接收數據,不會干擾當前頁面)。
Ajax工作原理
Ajax使瀏覽器和服務器之間多了一個Ajax引擎作為中間層。通過Ajax請求服務器時,Ajax會自行判斷哪些數據是需要提交到服務器,哪些不需要。只有確定需要從服務器讀取新數據時,Ajax引擎才會向服務器提交請求。
Ajax幾個特點
- 不需要提交整個頁面就可以更新數據。
- 與服務器異步通信。
- Ajax請求不能后退,瀏覽器沒有歷史記錄。
- Ajax請求的頁面不能加入到收藏夾。
Jquery中的Ajax
JQuery 對 Ajax 做了大量的封裝,不需要去考慮瀏覽器兼容性,使用起來也較為方便。
jquery對Ajax一共有三層封裝。
-
最底層:$.ajax()。
-
第二層:.load()、$.get()和$.post()。
-
最高層: $.getScript()和$.getJSON()方法。
$.Ajax()
$.Ajax()是所有Ajax方法中最底層的方法,其他的都是基於$.Ajax()方法的封裝,該方法只有一個參數-JQueryAjaxSettings
(功能鍵值對)。
$.Ajax
參數JQueryAjaxSettings
介紹:
參數 | 類型 | 說明 |
---|---|---|
url | String | 請求的地址 |
type | String | 請求方式:POST 或 GET,默認 GET |
timeout | Number | 設置請求超時的時間(毫秒) |
data | Object或String | 發送到服務器的數據,鍵值對或字符串 |
dataType | String | 從服務器返回的數據類型,比如 html、xml、json 等 |
beforeSend | Function | 發送請求前可修改 XMLHttpRequest 對象的函數 |
complete | Function | 請求完成后調用的回調函數 |
success | Function | 請求成功后調用的回調函數,先執行success再執行complete |
error | Function | 請求失敗時調用的回調函數,先執行error再執行complete |
global | Boolean | 默認為 true,表示是否觸發全局 Ajax |
cache | Boolean | 設置瀏覽器緩存響應,默認為 true。如果 dataType類型為 script 或 jsonp 則為 false。 |
content | DOM | 指定某個元素為與這個請求相關的所有回調函數的上下文。 |
contentType | String | 指 定 請 求 內 容 的 類 型 。 默 認 為application/x-www-form-urlencoded。 |
async | Boolean | 是否異步處理。默認為 true,false 為同步處理 |
processData | Boolean | 默認為 true,數據被處理為 URL 編碼格式。如果為 false,則阻止將傳入的數據處理為 URL 編碼的格式。 |
dataFilter | Function | 用來篩選響應數據的回調函數。 |
ifModified | Boolean | 默認為 false,不進行頭檢測。如果為true,進行頭檢測,當相應內容與上次請求改變時,請求被認為是成功的。 |
jsonp | String | 指定一個查詢參數名稱來覆蓋默認的 jsonp 回調參數名 callback。 |
username | String | 在 HTTP 認證請求中使用的用戶名 |
password | String | 在 HTTP 認證請求中使用的密碼 |
scriptCharset | String | 當遠程和本地內容使用不同的字符集時,用來設置 script 和 jsonp 請求所使用的字符集。 |
xhr | Function | 用來提供 XHR 實例自定義實現的回調函數 |
traditional | Boolean | 默認為 false,不使用傳統風格的參數序列化。如為 true,則使用 |
代碼示例:
$('button').click(function(){
$.ajax(
{
type:'post',
url:'test',
data:{
url:'hello',
},
dataType:'json',
success:function(data,stutas,xhr){
alert(data);
},
error:function(xhr, textStatus, data)){
alert(data);
},
complete:function(xhr,textStatus){
alert(textStatus);
}
}
)
});
$.Ajax的回調函數介紹:
- success
Function( Anything data, String textStatus, jqXHR jqXHR )
請求成功后執行的回調函數。
參數 | 類型 | 說明 |
---|---|---|
data | anything | 從服務器返回的數據,並根據dataType參數類型處理后的數據(默認是json) |
textStatus | string | 描述狀態的字符串 |
jqxhr | jqXHR | XMLHTTPRequest對象 |
- error
Function( jqXHR jqXHR, String textStatus, String errorThrown )
請求失敗是執行的回調函數
參數 | 類型 | 說明 |
---|---|---|
errorThrown | string | HTTP狀態的文本部分 |
textStatus | string | 描述錯誤信息的字符串 |
jqxhr | jqXHR | 描述發生錯誤類型的一個字符串 和 捕獲的異常對象 |
- complete
Function( jqXHR jqXHR, String textStatus )
請求完成后執行的回調函數,不管是成功還是失敗都執行。
參數 | 類型 | 說明 |
---|---|---|
errorThrown | string | HTTP狀態的文本部分 |
textStatus | string | 描述請求狀態的字符串 |
jqxhr | jqXHR | XMLHTTPRequest對象 |
$.load()
從服務器獲取數據並且將返回的HTML代碼插入至匹配的元素中。
$('Element').load(url,data,success(responseText,textStatus,XMLHttpRequest))
參數 | 類型 | 說明 |
---|---|---|
url | string | 必須 請求地址 |
data | Json或者string | 可選 請求數據 如果是json該load方法是post請求,默認是get請求 |
success | function | 當請求成功后執行的回調函數 |
responseText | string | 獲得字符串形式的響應數據 |
textStatus | string | 文本方式返回HTTP狀態碼 |
XMLHttpRequest | Object | xhr對象,有多種屬性 |
$.get()和$.post()
.load()
一般在獲取靜態資源時調用,$.get()
和$.post()
方法在需要和服務器交互數據時調用。
$.get()
方法通過 HTTP GET 請求載入信息。
這是$.ajax
GET請求的簡寫方式。請求成功時可調用回調函數。
$.get(url,data,success(response,status,xhr),dataType)
使用$.get()
從服務端獲取數據-代碼示例
定義model
public class PersonViewModel
{
public int PersonID { get; set; }
public string Name { get; set; }
public string PhoneNum { get; set; }
public bool IsMarried{get;set;}
}
定義Controller Action
public class MyAjaxController : Controller
{
public JsonResult PersonList()
{
IList<PersonViewModel> persons = new List<PersonViewModel>();
for (int i = 0; i < 10; i++)
{
persons.Add(new PersonViewModel() { Email = "email" + i, Name = "name", IsMarried = false, PhoneNum = "1234" + i, Home = CityEnum.BJ, Height = i });
}
return Json(persons,JsonRequestBehavior.AllowGet);
}
}
定義View
$.get('@Url.Action("PersonList", "MyAjax")',function (result) {
$.each(result, function (index, person) {
$('#myDiv').append('<p>Id: ' + person.PersonID + '</p>' +
'<p>Name: ' + person.Name + '</p>');
});
});
//在Jquery1.5版本后,新增了一些事件,可以更好的處理不同結果。
$.get('@Url.Action("PersonList", "MyAjax")')
.done(function (data) {
$.each(data, function (index, person) {
$('#myDiv').append('<p>Id: ' + person.PersonID + '</p>' +
'<p>Id: ' + person.Name + '</p>');
});
})
.fail(function (data) {
alert(data);
});
$.post()
方法通過 HTTP POST 請求從服務器載入數據。
$.post(url,data,success(data, textStatus, jqXHR),dataType)
使用$.post()
方法向服務端發送數據-代碼示例
定義一個Action
[HttpPost]
public JsonResult ToPersonList(IEnumerable<PersonViewModel> persons)
{
if (persons != null)
return Json(true);
else return Json(false);
}
定義一個View
<script>
var results = { persons : [{ "PersonID": "1", "Name": "Manas" },
{ "PersonID": "2", "Name": "Tester" }] };
$.post('@Url.Action("ToPersonList","MyAjax")',results, function (data) {
alert(data)
});;
//同樣也可以使用Jquery1.5版本的新的事件
$.post('@Url.Action("ToPersonList","MyAjax")', results)
.done(function (data) {
alert(data);
})
.fail(function (data) {
alert(data);
})
.always(function (data) {
alert(data);
})
</script>
$.get()
$.post()
方法都是四個參數,前面三個參數和$.load()
一樣,最后一個參數dataType:服務器返回的數據格式:xml、html、script、json、jsonp和text。只有第一個參數是必須的,其他都可以為空。
$.get()
$.post()
都是$.ajax()
的一個簡寫封裝,都是只能回調success狀態,error,和complete不能被回調。但是在jquery1.5版本上,新加了jqXHR.done()
(表示成功), jqXHR.fail()
(表示錯誤), 和 jqXHR.always()
事件,可以實現不同狀態的回調。
表單序列化
如果我們有一個復雜的表單,一個一個獲取表單數據是一個很瑣碎的事。jquery提供了一個表單的序列化方法serialize()
,會智能的獲取指定表單內的所有元素(包括單選框,復選框,下拉列表等)把表單內容序列化為字符串。此外serializeArray()
方法可以把數據整合為鍵值對的json對象。
如果我們需要多次調用$.ajax
方法,並且很多參數都相同,可以使用$.ajaxSetup()
方法,它會把一些公共的參數預先設置好,不用每次都設置。
$('form input[type=button]').click(function () {
$.ajaxSetup({
type : 'POST',
url : 'test',
data : $('form').serialize()
});
$.ajax({
success : function (response, status, xhr) {
alert(response);
});
});
在使用 data
屬性傳遞數據的時候,如果是以對象形式傳遞鍵值對,可以使用$.param()
方法將對象轉換為字符串鍵值對格式。
(主要是針對無法直接使用表單序列化方法.serialize()
,且傳遞參數為對象的情況)
MVC中的Ajax使用
Asp.Net MVC中包含了一組Ajax輔助方法。可以用來創建異步執行的表單和指向控制器操作的異步鏈接。當使用這個輔助方法時,不用編寫任何腳本代碼來實現程序的異步。該輔助方法依賴於非侵入式MVC的jquery擴展。如果使用這些輔助方法時,需要引入腳本jquery.unbotrusive-ajax.js
(可以在NuGet中獲得)
分部渲染
Asp.Net MVC中的分部頁面可以是partialPage
也可以是含有布局(layout
)的完整頁面。只是在return的時候返回類型是PartialView
。
絕大部分情況下,部分頁面的請求和完整頁面的請求是一樣的流程-請求被路由到指定控制器,控制器執行特定的業務邏輯,返回給對應的試圖。 我們可以在控制器中使用Request.IsAjax
來區別是否是ajax請求,是否是要返回分部試圖,還是完整試圖。分部試圖(return PartialView)是render和返回了該頁面的html。但是完整試圖(return View)是返回了包括頁面資源(css,js)和布局的所有html。
Ajax.Load()
異步加載一個分布頁面
- 定義一個ViewModel
//Model
[Bind(Exclude = "PersonID")]
public class PersonViewModel
{
[ScaffoldColumn(false)]
public int PersonID { get; set; }
[Display(Name = "姓名")]
[Required(ErrorMessage = "不能為空")]
public string Name { get; set; }
[Display(Name = "手機號")]
[Required(ErrorMessage = "不能為空")]
[DataType(DataType.PhoneNumber)]
public string PhoneNum { get; set; }
public bool IsMarried{get;set;}
}
- 定義主頁面View
//Main View:
@{
ViewBag.Title = "主頁面";
}
<h2>主頁面</h2>
<p>列表詳細信息</p>
<div id="partialDiv"></div>
<script>
$('#partialDiv').load('@Url.Action("ListPage", "MyAjax")')
</script>
定義分部頁面View
//分部頁面
@{
ViewBag.Title = "ListPage";
}
@model IList<WebApp.Models.PersonViewModel>
<h2>分布頁</h2>
<table class="table table-striped">
<thead>
@{ WebApp.Models.PersonViewModel p = null;}
<tr>
<th>@Html.LabelFor(m => @p.Email)</th>
<th>@Html.LabelFor(m => @p.Name)</th>
<th>@Html.LabelFor(m => @p.Home)</th>
<th>@Html.LabelFor(m => @p.IsMarried)</th>
<th>@Html.LabelFor(m => @p.Height)</th>
<th>@Html.LabelFor(m => @p.PhoneNum)</th>
@*也可以使用DisplayNameFor來顯示表頭*@
@*<th>@Html.DisplayNameFor(m => Model[0].Email)</th>
<th>@Html.DisplayNameFor(m => Model[0].Name)</th>
<th>@Html.DisplayNameFor(m => Model[0].IsMarried)</th>
<th>@Html.DisplayNameFor(m => Model[0].Height)</th>
<th>@Html.DisplayNameFor(m => Model[0].PhoneNum)</th>*@
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>@item.Email</td>
<td>@item.Name</td>
<td>@item.Home</td>
<td>@item.IsMarried</td>
<td>@item.Height</td>
<td>@item.PhoneNum</td>
</tr>
}
</tbody>
</table>
定義一個Action
//Controller
public class MyAjaxController : Controller
{
//主頁面
public ActionResult MainPage()
{
return View();
}
//分部頁面
public ActionResult ListPage()
{
IList<PersonViewModel> persons = new List<PersonViewModel>();
for (int i = 0; i < 10; i++)
{
persons.Add(new PersonViewModel() { Email = "email" + i, Name = "name", IsMarried = false, PhoneNum = "1234" + i, Home = CityEnum.BJ, Height = i });
}
if (Request.IsAjaxRequest())
{
return PartialView(persons);
}
return View(persons);
}
}
當請求主頁面的時候,會把分布頁面異步加載到主頁面的<div id="partialDiv"></div>
里
Ajax.ActionLink()
Ajax.ActionLink()輔助方法,可以異步請求加載頁面。
//Main view 主頁面
@{
ViewBag.Title = "MainPage";
}
<h2>主頁面</h2>
<p>列表詳細信息</p>
@Ajax.ActionLink("加載詳細列表", "ListPage", new AjaxOptions { UpdateTargetId = "partialDiv", InsertionMode = InsertionMode.Replace, HttpMethod = "Get" })
<div id="partialDiv"></div>
Asp.Net MVC 提供了多個AjaxOptions
的屬性,方法給我們使用,免去了不少js代碼。
如
名稱 | 說明 |
---|---|
Confirm | 獲取或設置在提交請求之前顯示在確認窗口中的消息。 |
HttpMethod | 獲取或設置 HTTP 請求方法(“Get”或“Post”)。 |
InsertionModel | 獲取或設置指定如何將響應插入目標 DOM 元素的模式。插入模式(“InsertAfter”、“InsertBefore”或“Replace”)。 默認值為“Replace”。 |
LoadingElementDuration | 獲取或設置一個值(以毫秒為單位),該值控制在顯示或隱藏加載元素時的動畫持續時間。 |
LoadingElementId | 獲取或設置在加載 Ajax 函數時要顯示的 HTML 元素的 id 特性。 |
OnBegin | 獲取或設置要在更新頁面之前立即調用的 JavaScript 函數的名稱 |
OnComplete | 獲取或設置在實例化響應數據之后但在更新頁面之前,要調用的 JavaScript 函數。 |
OnFailure | 獲取或設置在頁面更新失敗時要調用的 JavaScript 函數。 |
OnSuccess | 獲取或設置在成功更新頁面之后要調用的 JavaScript 函數。 |
UpdateTargetId | 獲取或設置要使用服務器響應來更新的 DOM 元素的 ID。 |
Url | 獲取或設置要向其發送請求的 URL。 |
Ajax表單提交
當我們使用jquery的ajax提交表單時,需要在click事件中添加e.preventDefault()
或者把<input type="submit" value="提交" />
改為<input type="button" value="提交" />
。否則會刷新頁面。如下代碼所示,
<form class="form-horizontal" role="form" method="post" id="myform">
<div>
<label for="i1">第一</label>
<input type="text" name="i1" id="i1" />
</div>
<div>
<label for="i2">第二</label>
<input type="text" name="i2" id="i2" />
</div>
<div>
<label for="i3">第三</label>
<input type="text" name="i3" id="i3" />
</div>
//或者使用<input type="button" value="提交" />,不必再阻止事件的傳遞了。
<input type="submit" value="提交" />
</form>
<script>
$("input[type=submit]").click(function (e) {
e.preventDefault();//阻止事件傳遞
$.post("@Url.Action("CheckNameByAjax")", $("#myform").serialize(), function (result) {
alert(result);
});
});
</script>
Asp.Net MVC提供了Ajax的表單輔助方法,可以更簡單快速的實現表單的ajax提交。
@using (Ajax.BeginForm("AjaxForm", "MyAjax", new AjaxOptions { HttpMethod = "Post", OnComplete = "foo", OnSuccess = "succ", OnFailure = "fail" }, new { role = "form" }))
{
<div>
<label for="i1">第一</label>
<input type="text" name="i1" id="i1" />
</div>
<div>
<label for="i2">第二</label>
<input type="text" name="i2" id="i2" />
</div>
<div>
<label for="i3">第三</label>
<input type="text" name="i3" id="i3" />
</div>
<input type="submit" value="提交" />
}
Ajax數據驗證
在注冊有時需要保證用戶名或者郵箱唯一或者是否合法,這個驗證又必須放在服務端完成。可以使用ajax異步請求,在用戶添加完用戶名或者郵箱的時候立即在服務端驗證並告知用戶結果,而不用填完整個表單,再去驗證唯一合法性。
- 定義一個ViewModel
//Model
[Bind(Exclude = "PersonID")]
public class PersonViewModel
{
[ScaffoldColumn(false)]
public int PersonID { get; set; }
[Display(Name = "姓名")]
[Required(ErrorMessage = "不能為空")]
public string Name { get; set; }
[Display(Name = "手機號")]
[Required(ErrorMessage = "不能為空")]
[DataType(DataType.PhoneNumber)]
public string PhoneNum { get; set; }
}
- 定義試圖View
//view
@model NameSpace.PersonViewModel
<form class="form-horizontal" role="form" method="post" id="myform">
<div>
<div class="form-group">
@Html.LabelFor(m => m.Name, new { @class = "control-label col-md-3" })
<div class="col-md-9">
@Html.TextBoxFor(m => m.Name, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Name, "", new { @class = "text-danger" })
</div>
</div>
<div>
<input type="submit" value="提交" class="btn btn-success" id="sure" />
</div>
</div>
</form>
<script>
$("#Name").change(function () {
$.ajax({
url: "@Url.Action("CheckUserName")",
type: "post",
data: { Name: $("#Name").val() },
dataType: "JSON",
success: function (response, stutas, xhr) {
alert(response+status + xhr.statusText);
},
error: function (xhr, stutas, response) {
alert(response + status + xhr.statusText);
},
complete: function (data) {
alert(data.status+data);
},
});
});
</script>
- 定義一個Action校驗用戶名的唯一和合法性
[HttpPost]
//參數一定要和ViewModel的屬性名稱一致
public JsonResult CheckUserName(string Name)
{
bool result = true;
if (Name == "admin")
{
result = false;
}
return Json(result);
}
至此我們實現了Ajax的用戶名唯一性和合法性的校驗。但是 Asp.Net MVC 提供了一個更簡單的方法,可以用更少的代碼實現一樣的功能
- 在屬性上添加
[Remote("MethodName", "ControllerName")]
特性
該特性允許客戶端調用服務端的方法。
修改Model
[Bind(Exclude = "PersonID")]
public class PersonViewModel
{
[ScaffoldColumn(false)]
public int PersonID { get; set; }
[Display(Name = "姓名")]
//添加Remote特性
[Remote("CheckUserName", "ControllerName",ErrorMessage="用戶名已存在")]
[Required(ErrorMessage = "不能為空")]
public string Name { get; set; }
[Display(Name = "手機號")]
[Required(ErrorMessage = "不能為空")]
[DataType(DataType.PhoneNumber)]
public string PhoneNum { get; set; }
}
我們只需添加一個Remote特性就可以實現用戶名的服務端驗證。節省了js的代碼。
- 修改Action
Asp.Net MVC默認是不允許Get請求Json(防止Json被劫持)。所以如果你需要Get請求Json。必須添加JsonRequestBehavior.AllowGet
。且該數據不那么重要。
//Action
public JsonResult CheckUserName(string Name)
{
//參數一定要和屬性名稱一致
bool result = true;
if (Name == "admin")
{
result = false;
}
//添加JsonRequestBehavior.AllowGet
return Json(result, JsonRequestBehavior.AllowGet);
}
Asp.Net MVC Ajax輔助方法可以讓我們更簡便的使用Ajax。但是也要理解本身Ajajx的請求。
如有不對,請多多指教。
參考資料: