ASP.NET Web API での HTML フォーム データの送信: フォーム urlencoded データ

パート 1: フォーム urlencoded データ

この記事では、フォーム urlencoded データを Web API コントローラーに投稿する方法を説明します。

HTML フォームの概要

HTML フォームでは、GET または POST を使用してサーバーにデータを送信します。 form 要素の 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);
            }
        }

    }
}

Note

このコントローラーはアクション ベースのルーティングを使用するため、ルート テンプレートは "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>

フォームの action 属性がコントローラー アクションの URI であることに注意してください。 いくつかの値が入力されたフォームを次に示します。

Screenshot of the Complex Type H T M L form with a Status field and a Date field filled in with values.

ユーザーが [送信] をクリックすると、ブラウザーは次のような 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 関数は、フォーム アクションを新しい関数に置き換えます。 これにより、[送信] ボタンの既定の動作がオーバーライドされます。 serialize 関数は、フォーム データを名前と値のペアにシリアル化します。 フォーム データをサーバーに送信するには、$.post() を呼び出します。

要求が完了すると、.success() または .error() ハンドラーはユーザーに適切なメッセージを表示します。

Screenshot of the Complex Type H T M L form with a local host error displayed in bold text to the user.

単純型の送信

前のセクションでは、複合型を送信しました。この複合型は、Web API によってモデル クラスのインスタンスに逆シリアル化されました。 文字列などの単純型を送信することもできます。

Note

単純型を送信する前に、代わりに複合型で値をラップすることを検討してください。 これにより、サーバー側でのモデル検証の利点が得られ、必要に応じてモデルを簡単に拡張できます。

単純型を送信する基本的な手順は同じですが、2 つの微妙な違いがあります。 まず、コントローラーで、パラメーター名を 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 に指示します。

Note

Web API は応答本文を最大 1 回読み取ります。そのため、要求本文から取得できるアクションのパラメーターは 1 つだけです。 要求本文から複数の値を取得する必要がある場合は、複合型を定義します。

次に、クライアントは次の形式で値を送信する必要があります。

=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