ASP.NET 使用Ajax

謙行發表於2013-08-07

之前在Ajax初步理解中介紹了對Ajax的初步理解,本文將介紹在ASP.NET中如何方便使用Ajax,第一種當然是使用jQuery的ajax,功能強大而且操作簡單方便,第二種是使用.NET封裝好的ScriptManager。

$.ajax向普通頁面傳送get請求

這是最簡單的一種方式了,先簡單瞭解jQuery ajax的語法,最常用的呼叫方式是這樣:$.ajax({settings}); 有幾個常用的setting,全部引數及其解釋可以去jQuery官方API文件查詢

1. type:請求方式 get/post

2. url:請求的Uri

3. async:請求是否為非同步

4. headers:自定義的header引數

5. data:發往伺服器的引數

6. dataType:引數格式,常見的有string、json、xml等

7. contents:決定怎樣解析response的一個”字串/正規表示式” map

8. contentType:傳送到伺服器的額資料的內容編碼型別,它的預設值是"application/x-www-form-urlencoded; charset=UTF-8""。

9. success:請求成功後呼叫的控制程式碼

10.error:請求失敗後呼叫的控制程式碼

沒使用過jQuery的ajax話這樣看有些雲裡霧裡的感覺,來看一個簡單例子

首先使用Visual Studio新建一個WebApplication,把jQuery.js引入project,然後新增兩個頁面,Default.aspx作為測試用

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Web.Default" %>
<!DOCTYPE html >
<html>
<head runat="server">
    <title>Ajax</title>
    <script src="jQuery.js" type="text/javascript"></script>
    <style type="text/css">
        html, body, form
        {
            width: 100%;
            height: 100%;
            padding: 0px;
            margin: 0px;
        }
        
        #container
        {
            margin: 100px;
            height: 300px;
            width: 500px;
            background-color: #eee;
            border: dached 1px #0e0;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <div id="container">
        <input type="button" value="Test Ajax" onclick="testGet()" />
        <br />
    </div>
    <script type="text/javascript">
        function setContainer(text) {
            document.getElementById("container").innerHTML += ('<br/>' + text);
        }

        function testGet() {
            $.ajax({
                type: 'get',
                url: 'NormalPage.aspx',
                async: true,
                success: function (result) {
                    alert(result);
                },
                error: function () {
                    setContainer('ERROR!');
                }
            });
        }
    </script>
    </form>
</body>
</html>

NormalPage.aspx作為請求頁面,先不做任何處理。在Default.aspx頁面中的JavaScript中可以看到testGet函式就利用jQuery的ajax向Normal.aspx傳送了了一個get請求,沒寫的引數使用jQuery預設引數,這個呼叫沒使用任何引數,簡單向Normal.aspx頁面傳送請求,請求成功則alert全部response(即success方法引數:result,jQuery會把responseText傳入success方法第一個引數),請求失敗則向DIV中新增一行錯誤提示文字。如果一切正常,可以看到頁面彈出對話方塊,對話方塊內內容即是Normal.aspx頁面內容

image

一個簡單的get請求完成了,這樣的結果一般沒有多大用處,也不是ajax意圖所在,使用Ajax主要是想使用JavaScript可以非同步向伺服器傳送特定請求,獲取伺服器相關資料,比如向伺服器詢問天氣,然後獲得天氣資料,更新頁面,而不是獲取整個頁面,換句話說,使用Ajax本身就是為了擺脫更新整個頁面來更新頁面資料這種模式,僅僅需要伺服器給我們資料即可,這就需要呼叫伺服器端的特定方法。

$.ajax GET請求呼叫伺服器特定方法

我們這時候需要修改NormalPage.aspx,為其新增幾個方法供Default.aspx測試呼叫

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Web
{
    public partial class NormalPage : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string action = Request.QueryString["action"];
            Response.Clear(); //清除所有之前生成的Response內容
            if (!string.IsNullOrEmpty(action))
            {
                switch (action)
                {
                    case "getTime":
                        Response.Write(GetTime());
                        break;
                    case "getDate":
                        Response.Write(GetDate());
                        break;
                }
            }
            Response.End(); //停止Response後續寫入動作,保證Response內只有我們寫入內容
        }


        private string GetDate()
        {
            return DateTime.Now.ToShortDateString();
        }

        private string GetTime() 
        {
            return DateTime.Now.ToShortTimeString();
        }
    }
}

然後為Default.aspx新增一個新的方法,並修改button的onclick方法為新寫的函式

function testGet2() {
            $.ajax({
                type: 'get',
                url: 'NormalPage.aspx',
                async: true,
                data:{action:'getTime'},
                success: function (result) {
                    setContainer(result);
                },
                error: function () {
                    setContainer('ERROR!');
                }
            });
        }

testGet2函式是在testGet函式的基礎上做了些許修改,首先對success方法做了更改,把得到的response寫到頁面;然後對請求新增了data引數,請求向伺服器傳送了一個action:getTime的鍵值對,在get請求中jQuery會把此引數轉為url的引數,上面寫法和這種寫法效果一樣

function testGet3() {
            $.ajax({
                type: 'get',
                url: 'NormalPage.aspx?action=getTime',
                async: true,
                success: function (result) {
                    setContainer(result);
                },
                error: function () {
                    setContainer('ERROR!');
                }
            });
        }

看一下執行效果,這是Chrome的監視結果

image

如果除錯我們發現這個請求呼叫的伺服器頁面NormalPage.aspx的GETime方法,並且response中只包含對有用的資料,如果把請求中引數的值改為getDate,那麼就會呼叫對應GetDate方法。

$.ajax POST與json

這樣向一個頁面傳送請求然後在Load事件處理程式中根據引數呼叫不同方法,清除Response,寫入Response,終止Response,而且傳入的引數侷限性太大,好業餘的趕腳,看看專業些解決方法。為project新增一個General Handler型別檔案,關於HttpHandler相關內容本文不做詳細解釋,只需知道它可以非常輕量級的處理HTTP請求,不用走繁瑣的頁面生命週期處理各種非必需資料。

Handler.ashx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Newtonsoft.Json;

namespace Web
{
    /// <summary>
    /// Summary description for Handler
    /// </summary>
    public class Handler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            Student stu = new Student();
            int Id = Convert.ToInt32(context.Request.Form["ID"]);
            if (Id == 1)
            {
                stu.Name = "Byron";
            }
            else
            {
                stu.Name = "Frank";
            }
           string stuJsonString= JsonConvert.SerializeObject(stu);
           context.Response.Write(stuJsonString);
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

關於這個類語法本文不做詳細說明,每次發起HTTP請求ProcessRequest方法都會被呼叫到,Post型別請求引數和一再Request物件的Form中取得,每次根據引數ID值返回對應json物件字串,為了展示json格式資料互動,需要為專案引入json.net這一開源類庫處理物件序列化反序列化問題,然後建立一個Student類檔案

Student.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Web
{
    public class Student
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
}

看看頁面如何處理

function testPost() {
            $.ajax({
                type: 'post',
                url: 'Handler.ashx',
                async: true,
                data: { ID: '1' },
                success: function (result) {
                    setContainer(result);
                    var stu =eval ('('+result+')');
                    setContainer(stu.ID);
                    setContainer(stu.Name);
                },
                error: function () {
                    setContainer('ERROR!');
                }
            });
        }

結果是這個樣子的

image

上面程式碼向Handler.ashx傳送一Post請求,比且帶有引數{ID:’1’},可以看到結果,如果用除錯工具可以發現,得到的result是一個json格式的字串,也就是往Response寫的物件序列化後的結果。這樣就實現了比較專業些的方式呼叫Ajax,但是有一個問題依舊存在,HttpHandler會自動呼叫ProcessRequest方法,但是也只能呼叫該方法,如果想呼叫不同方法只能像普通頁面那樣傳遞一個參數列明呼叫哪個方法,或者寫不同的Handler檔案。

WebService與ScriptManager

微軟向來很貼心,看看微軟怎麼處理上面的困惑,那就是利用WebService,WebService配合SCriptManager有客戶端呼叫的能力,在專案中新增一個Webservice檔案

WebService.asmx

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

namespace Web
{
    /// <summary>
    /// Summary description for WebService
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    [System.Web.Script.Services.ScriptService]
    public class WebService : System.Web.Services.WebService
    {

        [WebMethod]
        public Student GetStudent(int  ID)
        {
            if (ID == 1)
            {
                return new Student() { ID = 1, Name = "Byron" };
            }
            else
            {
                return new Student() { ID = 2, Name = "Frank" };
            }
        }

        

        [WebMethod]
        public string GetDateTime(bool isLong)
        {
            if (isLong)
            {
                return DateTime.Now.ToLongDateString();
            }
            else
            {
                return DateTime.Now.ToShortDateString();
            }
        }

    }
}

程式碼中加黃的code預設是被註釋掉的,要想讓客戶端呼叫需要把註釋去掉,Service中定義了兩個方法,寫個測試方法讓客戶端呼叫第一個方法根據引數返回對應物件,首先需要在頁面from內加上ScriptManager,引用剛才寫的WebService檔案

Default.aspx

<form id="form1" runat="server">
    <asp:ScriptManager ID="clientService" runat="server">
        <Services>
            <asp:ServiceReference Path="~/WebService.asmx" />
        </Services>
    </asp:ScriptManager>
    <div id="container">
        <input type="button" value="Test Ajax" onclick="testPost2()" />
        <br />
    </div>
...

然後新增JavaScript測試程式碼

function testPost2() {
            Web.WebService.GetStudent(1, function (result) {
                setContainer(result.ID);
                setContainer(result.Name);
            }, function () {
                setContainer('ERROR!');
            });
        }

測試程式碼中需要顯示書寫WebService定義方法完整路徑,WebService名稱空間.WebService類名.方法名,而出入的引數列表前幾個是呼叫方法的引數列表,因為GetStudent只有一個引數,所以只寫一個,如果有兩個引數就順序寫兩個,另外兩個引數可以很明顯看出來是響應成功/失敗處理程式。看看執行結果:

image

觀察仔細會發現使用ScriptManager和WebService組合有福利,在WebService中傳回Student物件的時候並沒有序列化成字串,而是直接返回,看上面圖發現物件已經自動轉換為一json物件,result結果可以直接操作,果真非常貼心。而上一個例子中我們得到的response是一個json字串,在客戶端需要用eval使其轉換為json物件。

ScriptManager+WebSefvice呼叫ajax帶來了很大的便利性,但同時犧牲了很多靈活性,我們沒法像jQuery那樣指定很多設定有沒有兩全其美的辦法呢

$.ajax+WebService

jQuery呼叫Handler幾乎完美了,但是不能處理多個方法,上面例子我們可以發現WebService可以實現這一功能,那麼能不能jQUery呼叫WebService的不同方法呢?答案是肯定的,試一試用jQuery呼叫剛才WebService定義的第二個方法。寫一個測試函式

function testPost3() {
            $.ajax({
                type: 'post',
                url: 'WebService.asmx/GetDateTime',
                async: true,
                data: { isLong: true },
                success: function (result) {
                    setContainer($(result).find('string').text());
                },
                error: function () {
                    setContainer('ERROR!');
                }
            });
        }

呼叫方式沒有多大變化,簡單依舊,只要把URL改為WebService路徑+需要呼叫的方法名,然後把引數放到data裡就可以了。我們看看結果:

image

通過上圖可以看到,jQuery呼叫WebService預設會返回一個XML文件,而需要的資料在 <string>節點中,只需要使用jQuery解析xml的語法就可以輕鬆得到資料。如果希望返回一個json物件怎麼辦?那就得和呼叫Handler一樣使用json.net序列化,然後前端使用eval轉換了,也不會過於複雜。我在專案中最常使用這個模式,這樣既保持了jQuery的靈活性又可以在一個Service中書寫多個方法供呼叫,還不用走複雜的頁面生命週期

json.net和本文示例原始碼

json.net是一個開源的.net平臺處理json的庫,可以序列化Dictionay巢狀等複雜物件,關於其簡單使用有時間會總結一下,可以自codeplex上得到其原始碼和官方說明。本文的原始碼可以點選這裡獲得。

 

相關文章