在 ASP.NET Web API中傳送 HTML 表單資料:Form-urlencoded 資料

第 1 部分:表單 URLencoded 資料

本文說明如何將表單 URL 編碼的資料張貼至 Web API 控制器。

HTML 表單概觀

HTML 表單會使用 GET 或 POST 將資料傳送至伺服器。 表單元素的method屬性會提供 HTTP 方法:

<form action="api/values" method="post">

預設方法是 GET。 如果表單使用 GET,表單資料會在 URI 中編碼為查詢字串。 如果表單使用 POST,表單資料會放在要求本文中。 針對 POSTed 資料, enctype 屬性會指定要求本文的格式:

enctype 描述
application/x-www-form-urlencoded 表單資料會編碼為名稱/值組,類似于 URI 查詢字串。 這是 POST 的預設格式。
multipart/form-data 表單資料會編碼為多部分 MIME 訊息。 如果您要將檔案上傳至伺服器,請使用此格式。

本文的第 1 部分會探討 x-www-form-urlencoded 格式。 第 2 部分 描述多部分 MIME。

傳送複雜類型

一般而言,您會傳送複雜類型,由來自數個表單控制項的值所組成。 請考慮下列代表狀態更新的模型:

namespace FormEncode.Models
{
    using System;
    using System.ComponentModel.DataAnnotations;

    public class Update
    {
        [Required]
        [MaxLength(140)]
        public string Status { get; set; }

        public DateTime Date { get; set; }
    }
}

以下是透過 POST 接受 Update 物件的 Web API 控制器。

namespace FormEncode.Controllers
{
    using FormEncode.Models;
    using System;
    using System.Collections.Generic;
    using System.Net;
    using System.Net.Http;
    using System.Web;
    using System.Web.Http;

    public class UpdatesController : ApiController
    {
        static readonly Dictionary<Guid, Update> updates = new Dictionary<Guid, Update>();

        [HttpPost]
        [ActionName("Complex")]
        public HttpResponseMessage PostComplex(Update update)
        {
            if (ModelState.IsValid && update != null)
            {
                // Convert any HTML markup in the status text.
                update.Status = HttpUtility.HtmlEncode(update.Status);

                // Assign a new ID.
                var id = Guid.NewGuid();
                updates[id] = update;

                // Create a 201 response.
                var response = new HttpResponseMessage(HttpStatusCode.Created)
                {
                    Content = new StringContent(update.Status)
                };
                response.Headers.Location = 
                    new Uri(Url.Link("DefaultApi", new { action = "status", id = id }));
                return response;
            }
            else
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest);
            }
        }

        [HttpGet]
        public Update Status(Guid id)
        {
            Update update;
            if (updates.TryGetValue(id, out update))
            {
                return update;
            }
            else
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }

    }
}

注意

此控制器使用 動作型路由,因此路由範本為 「api/{controller}/{action}/{id}」。 用戶端會將資料張貼到 「/api/updates/complex」。

現在讓我們撰寫 HTML 表單,讓使用者提交狀態更新。

<h1>Complex Type</h1>
<form id="form1" method="post" action="api/updates/complex" 
    enctype="application/x-www-form-urlencoded">
    <div>
        <label for="status">Status</label>
    </div>
    <div>
        <input name="status" type="text" />
    </div>
    <div>
        <label for="date">Date</label>
    </div>
    <div>
        <input name="date" type="text" />
    </div>
    <div>
        <input type="submit" value="Submit" />
    </div>
</form>

請注意,表單上的 動作 屬性是控制器動作的 URI。 以下是一些輸入值的表單:

[複雜類型 H T M L] 表單的螢幕擷取畫面,其中 [狀態] 欄位和填入值的 [日期] 欄位。

當使用者按一下 [提交] 時,瀏覽器會傳送類似下列的 HTTP 要求:

POST http://localhost:38899/api/updates/complex HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Content-Type: application/x-www-form-urlencoded
Content-Length: 47

status=Shopping+at+the+mall.&date=6%2F15%2F2012

請注意,要求本文包含格式資料,格式為名稱/值組。 Web API 會自動將名稱/值組轉換成 類別的 Update 實例。

透過 AJAX 傳送表單資料

當使用者提交表單時,瀏覽器會從目前的頁面巡覽,並轉譯回應訊息的本文。 當回應是 HTML 頁面時,這沒問題。 不過,使用 Web API 時,回應主體通常是空的,或包含結構化資料,例如 JSON。 在此情況下,使用 AJAX 要求傳送表單資料更合理,讓頁面可以處理回應。

下列程式碼示範如何使用 jQuery 張貼表單資料。

<script type="text/javascript">
    $("#form1").submit(function () {
        var jqxhr = $.post('api/updates/complex', $('#form1').serialize())
            .success(function () {
                var loc = jqxhr.getResponseHeader('Location');
                var a = $('<a/>', { href: loc, text: loc });
                $('#message').html(a);
            })
            .error(function () {
                $('#message').html("Error posting the update.");
            });
        return false;
    });
</script>

jQuery submit 函式會將表單動作取代為新的函式。 這會覆寫 [提交] 按鈕的預設行為。 序列化函式會將表單資料 序列化 成名稱/值組。 若要將表單資料傳送至伺服器,請呼叫 $.post()

當要求完成時, .success().error() 處理常式會向使用者顯示適當的訊息。

[複雜類型 H T M L] 表單的螢幕擷取畫面,其中顯示粗體文字給使用者的本機主機錯誤。

傳送簡單類型

在前幾節中,我們傳送了複雜類型,Web API 會將它還原序列化至模型類別的實例。 您也可以傳送簡單的類型,例如字串。

注意

在傳送簡單類型之前,請考慮改為將值包裝在複雜類型中。 這可讓您在伺服器端驗證模型的優點,並在需要時更輕鬆地擴充模型。

傳送簡單類型的基本步驟相同,但有兩個細微的差異。 首先,在控制器中,您必須使用 FromBody 屬性裝飾參數名稱。

[HttpPost]
[ActionName("Simple")]
public HttpResponseMessage PostSimple([FromBody] string value)
{
    if (value != null)
    {
        Update update = new Update()
        {
            Status = HttpUtility.HtmlEncode(value),
            Date = DateTime.UtcNow
        };

        var id = Guid.NewGuid();
        updates[id] = update;

        var response = new HttpResponseMessage(HttpStatusCode.Created)
        {
            Content = new StringContent(update.Status)
        };
        response.Headers.Location = 
            new Uri(Url.Link("DefaultApi", new { action = "status", id = id }));
        return response;
    }
    else
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }

根據預設,Web API 會嘗試從要求 URI 取得簡單類型。 FromBody屬性會告訴 Web API 從要求本文讀取值。

注意

Web API 最多會讀取回應本文一次,因此動作只能有一個參數來自要求本文。 如果您需要從要求本文取得多個值,請定義複雜類型。

其次,用戶端必須以下列格式傳送值:

=value

具體來說,名稱/值組的名稱部分必須是空白的簡單類型。 並非所有瀏覽器都支援 HTML 表單,但您會在腳本中建立此格式,如下所示:

$.post('api/updates/simple', { "": $('#status1').val() });

以下是範例表單:

<h1>Simple Type</h1>
<form id="form2">
    <div>
        <label for="status">Status</label>
    </div>
    <div>
        <input id="status1" type="text" />
    </div>
    <div>
        <input type="submit" value="Submit" />
    </div>
</form>

以下是提交表單值的腳本。 前一個腳本的唯一差異是傳遞至 post 函式的引數。

$('#form2').submit(function () {
    var jqxhr = $.post('api/updates/simple', { "": $('#status1').val() })
        .success(function () {
            var loc = jqxhr.getResponseHeader('Location');
            var a = $('<a/>', { href: loc, text: loc });
            $('#message').html(a);
        })
        .error(function () {
            $('#message').html("Error posting the update.");
        });
    return false;
});

您可以使用相同的方法來傳送簡單類型的陣列:

$.post('api/updates/postlist', { "": ["update one", "update two", "update three"] });

其他資源

第 2 部分:檔案上傳和多部分 MIME