Enviando dados de formulário HTML em ASP.NET Web API: formulário-dados urlencoded

por Mike Wasson

Parte 1: formulário – dados urlencoded

Este artigo mostra como postar dados de formulário urlencoded em um controlador de API da Web.

Visão geral de formulários HTML

Os formulários HTML usam GET ou POST para enviar dados ao servidor. O atributo Method do elemento Form fornece o método http:

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

O método padrão é GET. Se o formulário usar GET, os dados do formulário serão codificados no URI como uma cadeia de caracteres de consulta. Se o formulário usar POST, os dados do formulário serão colocados no corpo da solicitação. Para dados postados, o atributo Enctype especifica o formato do corpo da solicitação:

Enctype DESCRIÇÃO
Application/x-www-form-urlencoded Os dados de formulário são codificados como pares de nome/valor, semelhante a uma cadeia de caracteres de consulta de URI. Este é o formato padrão para POST.
multipart/form-data Os dados de formulário são codificados como uma mensagem MIME de várias partes. Use esse formato se você estiver carregando um arquivo para o servidor.

A parte 1 deste artigo analisa o formato x-www-form-urlencoded. A parte 2 descreve o MIME de várias partes.

Enviando tipos complexos

Normalmente, você enviará um tipo complexo, composto por valores extraídos de vários controles de formulário. Considere o seguinte modelo que representa uma atualização de status:

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; }
    }
}

Aqui está um controlador de API da Web que aceita um objeto Update por meio de 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);
            }
        }

    }
}

Note

Esse controlador usa o roteamento baseado em ação, portanto, o modelo de rota é "API/{Controller}/{Action}/{id}". O cliente irá postar os dados para "/API/updates/Complex".

Agora, vamos escrever um formulário HTML para que os usuários enviem uma atualização de status.

<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>

Observe que o atributo Action no formulário é o URI da nossa ação do controlador. Aqui está o formulário com alguns valores inseridos em:

Quando o usuário clica em enviar, o navegador envia uma solicitação HTTP semelhante à seguinte:

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

Observe que o corpo da solicitação contém os dados do formulário, formatados como pares de nome/valor. A API da Web converte automaticamente os pares de nome/valor em uma instância da classe Update.

Enviando dados de formulário via AJAX

Quando um usuário envia um formulário, o navegador navega para fora da página atual e renderiza o corpo da mensagem de resposta. Tudo bem quando a resposta é uma página HTML. Com uma API Web, no entanto, o corpo da resposta geralmente está vazio ou contém dados estruturados, como JSON. Nesse caso, faz mais sentido enviar os dados do formulário usando uma solicitação AJAX, para que a página possa processar a resposta.

O código a seguir mostra como postar dados de formulário usando o 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>

A função de envio do jQuery substitui a ação de formulário por uma nova função. Isso substitui o comportamento padrão do botão enviar. A função Serialize serializa os dados do formulário em pares de nome/valor. Para enviar os dados do formulário para o servidor, chame $.post().

Quando a solicitação for concluída, o manipulador .success() ou .error() exibirá uma mensagem apropriada para o usuário.

Enviando tipos simples

Nas seções anteriores, enviamos um tipo complexo, que API da Web é desserializada para uma instância de uma classe de modelo. Você também pode enviar tipos simples, como uma cadeia de caracteres.

Note

Antes de enviar um tipo simples, considere encapsular o valor em um tipo complexo. Isso oferece os benefícios da validação de modelo no lado do servidor e torna mais fácil estender seu modelo, se necessário.

As etapas básicas para enviar um tipo simples são as mesmas, mas há duas diferenças sutis. Primeiro, no controlador, você deve decorar o nome do parâmetro com o atributo 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);
    }

Por padrão, a API Web tenta obter tipos simples do URI de solicitação. O atributo FromBody informa à API da Web para ler o valor do corpo da solicitação.

Note

A API Web lê o corpo da resposta no máximo uma vez, de modo que apenas um parâmetro de uma ação pode vir do corpo da solicitação. Se você precisar obter vários valores do corpo da solicitação, defina um tipo complexo.

Em segundo lugar, o cliente precisa enviar o valor com o seguinte formato:

=value

Especificamente, a parte do nome do par nome/valor deve estar vazia para um tipo simples. Nem todos os navegadores dão suporte a isso para formulários HTML, mas você cria esse formato em script da seguinte maneira:

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

Veja um exemplo de formulário:

<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>

E aqui está o script para enviar o valor do formulário. A única diferença do script anterior é o argumento passado para a função 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;
});

Você pode usar a mesma abordagem para enviar uma matriz de tipos simples:

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

Recursos adicionais

Parte 2: carregamento de arquivo e MIME de várias partes