Поделиться через


Отправка данных html-формы в веб-API ASP.NET: данные с кодом формы

Часть 1. Данные с кодом формы

В этой статье показано, как отправлять данные form-urlencoded в контроллер веб-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; }
    }
}

Ниже приведен контроллер веб-API, который принимает Update объект через POST.

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

Обратите внимание, что текст запроса содержит данные формы, отформатированные в виде пар "имя-значение". Веб-API автоматически преобразует пары "имя-значение" в экземпляр Update класса .

Отправка данных формы с помощью AJAX

Когда пользователь отправляет форму, браузер переходит с текущей страницы и отображает текст ответного сообщения. Это нормально, если ответ представляет собой HTML-страницу. Однако при использовании веб-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() выводит пользователю соответствующее сообщение.

Снимок экрана: форма сложного типа H T M L с ошибкой локального узла, отображаемой для пользователя полужирным шрифтом.

Отправка простых типов

В предыдущих разделах мы отправили сложный тип, который веб-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);
    }

По умолчанию веб-API пытается получить простые типы из URI запроса. Атрибут FromBody сообщает веб-API, что нужно считывать значение из текста запроса.

Примечание

Веб-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