前篇文章提到RESTful Web Service的幾點特性:

  1. 使用URI來代表目標資源,在CRUD情境中目標資源就是要查詢或更新的資料。
  2. 使用不同的HTTP Method來區別操作,使用GET、POST、PUT、DELETE分別代表查詢、新增、修改、刪除動作。
  3. 除了HTTP Method,HTTP Status Code也用來表示執行結果,例如傳回HTTP 404表示查詢不到;當新增資料完成傳回HTTP 201並在Response Header使用Location指向查詢該筆資料的URI。

第1點是單純的Server端Routing要求,而2,3點需要Server及Client雙方都支援。在ASP.NET中,Request.HttpMethod可用來判別Client端指定的方法,指定Response.StatusCode則可指定201, 404, 500等不同狀態碼,而201時需要在Response Header加料的需求,可透過Response.AppendHeader()達成,這些對ASP.NET來說都是一塊蛋糕,毋需擔心。回到Client端,過去在寫jQuery,常使用的$.get(), $.post()並無法滿足自訂HTTP Method及處理特定Status Code的需求,但不用擔心,回歸到彈性十足的$.ajax(),就能輕鬆搞定。

要呼叫RESTful Web Service,我們會用到jQuery.ajax()的幾項設定參數:

  • url
    指向資源的URL位址
  • type
    可傳入"GET", "POST", "PUT", "DELETE",指定發出Request的HTTP Method
  • contentType
    指定Request內容的MIME型別,在POST/PUT/DELETE時,多半需傳送待新增刪資料物件的JSON字串,故多會設成"application/json; charset=UTF-8"供Server端識別。
  • data
    在POST/PUT/DELETE時多會使用data: JSON.stringify(dataObject)的方式,將Client端資料物件轉為JSON字串後抛至Server端。
  • statusCode
    可指定當前端傳回特定狀態碼時要執行的動作,例如:
    statusCode: { 
        201: function(data, statusText, xhr) { … }
        404: function(xhr, statusText, err) { … },
        500: function(xhr, statusText, err) { … }
    }
    代表當回傳結果是201, 404及500時,分別執行三段不同邏輯。要注意的是,在加掛statusCode事件時,若網頁回傳結果正常(指Status Code=2xx或304),sucess: function(data, statusText, xhr)一樣會被觸發;若狀態碼不等於2xx與304,則error: function(xhr, statusTExt, err)也會被觸發。所以statusCode裡宣告的事件會被額外執行,而非取代原有的success及error,且傳入的參數也會依狀態碼而有所不同,當狀態碼為2xx或304時,比照success,會傳入data, statusText及xhr;反之則比照error,可取得xhr, statusText及err。
    前述的xhr物件,另外提供了responseText/responseXML及getResponseHeader()... 等,可用來取得傳回結果的原始資料及Response Header。

以下是我整理好的$.ajax() REST應用範例。分別測試HttpMethod == "DELETE"、回傳StatusCode=404、StatusCode=201並傳回Location Header、直接丟出Exception、使用StatusCode=500表示出錯並傳回錯誤資訊。這裡先不導入ASP.NET Routing,直接用WebForm.aspx?test=…的方式執行各種測試,我另外還多提供直接用Iframe導向WebForm.aspx?test=...顯示網頁,做為與$.ajax()存取做法的對照。

    <%@ Page Language="C#" %>
    <script runat="server">
        void Page_Load(object sender, EventArgs e)
        {
            string testItem = Request["test"];
            if (!string.IsNullOrEmpty(testItem))
            {
                //測試1, 顯示HTTP Method
                if (testItem == "method")
                    Response.Write("HttpMethod=" + Request.HttpMethod);
                //測試2, 傳回HTTP 404
                else if (testItem == "404")
                    Response.StatusCode = 404;
                //測試3, 傳回HTTP 201及Location header
                else if (testItem == "201")
                {
                    Response.StatusCode = 201;
                    Response.AppendHeader("Location",
                        "http://blog.darkthread.net");
                }
                //測試3, 故意引發錯誤,傳回ASP.NET預設錯誤頁
                else if (testItem == "error")
                    throw new ApplicationException("故意錯誤");
                //測試4, 抛回Client端可解讀的HTTP 500
                else if (testItem == "errorInfo")
                {
                    Response.StatusCode = 500;
                    Response.Write("我達達的馬蹄是個美麗的錯誤~");
                }
                Response.End();
            }
        }
    </script>
    <!DOCTYPE>
     
    <html>
    <head id="Head1" runat="server">
        <title>AJAX Example</title>
        <style>
            body,input { font-size: 9pt; }
        </style>
        <script src='http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js'></script>   
        <script>
            $(function () {
                $("#btnGet").click(function () {
                    $("#frmShow").attr("src",
                        "?test=" + $("#selTestItem").val());
                });
                $("#btnAjax").click(function () {
                    var test = $("#selTestItem").val();
                    var $disp = $("#dvStatus");
                    $disp.text("");
                    $.ajax({
                        url: "?test=" + test,
                        //測試method時使用DELETE,其餘時候傳回P
                        type: test == "method" ? "DELETE" : "POST",
                        contentType: "application/json; charset=UTF-8",
                        statusCode: { //依不同StatusCode執行不同邏輯
                            200: function (r) { alert("OK-" + r); },
                            201: function (res, stausText, xhr) {
                                alert("201-Location=" + 
                                    //透過XHR取出Response Header
                                    xhr.getResponseHeader("Location"));
                            },
                            404: function () {
                                alert("Page Not Found!");
                            },
                            500: function (xhr, statusText, err) {
                                alert(xhr.responseText);
                            }
                        },
                        error: function (xhr, statusText, err) {
                            //StatusCode=2xx或304時執行success, 其餘則將觸發error
                            $disp.text("ERROR->" + statusText + "/" + err);
                        }
                    });
     
                });
            });
        </script>
    </head>
    <body>
        <form id="form1" runat="server">
        測試項目: 
        <select id="selTestItem">
            <option value="method">DELETE Method</option>
            <option value="404">傳回404</option>
            <option value="201">傳回201及Header</option>
            <option value="error">發生錯誤</option>
            <option value="errorInfo">解析錯誤</option>
        </select>
        <input type="button" id="btnGet" value="直接瀏覽" />
        <input type="button" id="btnAjax" value="AJAX存取" />
        <hr />
        <div id="dvStatus"></div>
        <iframe id="frmShow" style="width: 350px; height: 300px"></iframe>
        </form>
    </body>
    </html>

    執行結果如下:

    1) 用IFrame直接瀏覽?test=delete,無法指定HttpMethod,故HttpMethod==GET

    2) 直接瀏覽?test=201,StatusCode=201與一般正常執行無異,也無法存取Location資訊

    3) 直接瀏覽?test=404,如同一般找不到網頁的情境

    4) 直接瀏覽?test=error,因未開web.config customError,故看到ASP.NET預設的錯誤畫面

    5) 直接瀏覽?test=errorInfo,可看出網站出錯,但看不到額外傳回的訊息

    6) 使用$.ajax() type: "DELETE",Server端成功解讀HttpMethod

    7) $.ajax()攔截到statusCode 404,而頁面上<div id=”dvStatud”>顯示"ERROR->error/Not Found”,表示error: function()也被觸發了。

    8) $.ajax()攔載到statusCode 201,也成功由Response Header取出Location資訊

    9) $.ajax()接收ASP.NET例外網頁,error被觸發,但訊息內容為HTML,Client程式難以解析

    10) Server端回傳StatusCode 500並Response.Write特定文字,$.ajax()偵錯到error且能讀取訊息

    用$.ajax()搞定Client端呼叫REST的各項要求後,接下來就要回到主場ASP.NET 3.5迎接建立RESTful Web Service的挑戰囉~


    Comments

    Be the first to post a comment

    Post a comment