Asp.Net MVC 使用 Ajax

JoeSnail發表於2017-11-13

Asp.Net MVC 使用 Ajax

Ajax

簡單來說Ajax是一個無需重新載入整個網頁的情況下,可以更新區域性頁面或資料的技術(非同步的傳送接收資料,不會干擾當前頁面)。

Ajax工作原理

Ajax使瀏覽器和伺服器之間多了一個Ajax引擎作為中間層。通過Ajax請求伺服器時,Ajax會自行判斷哪些資料是需要提交到伺服器,哪些不需要。只有確定需要從伺服器讀取新資料時,Ajax引擎才會向伺服器提交請求。

Ajax原理

Ajax幾個特點

  1. 不需要提交整個頁面就可以更新資料。
  2. 與伺服器非同步通訊。
  3. Ajax請求不能後退,瀏覽器沒有歷史記錄。
  4. 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()輔助方法,可以非同步請求載入頁面。

//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的請求。


如有不對,請多多指教。


參考資料:

相關文章