Partilhar via


Envio de Dados de Formulário HTML na API Web ASP.NET: Dados Form-urlencoded

Parte 1: Dados Form-urlencoded

Este artigo mostra como publicar dados de formulário codificados em URL para um controlador da API Web.

Visão geral dos formulários HTML

Formulários HTML utilizam GET ou POST para enviar dados ao servidor. O atributo método do elemento de forma 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 são codificados no URI como uma cadeia de consulta. Se o formulário usar POST, os dados do formulário são colocados no corpo do pedido. Para dados POSTed, o atributo enctype especifica o formato do corpo do pedido:

enctype Descrição
application/x-www-form-urlencoded Os dados do formulário são codificados como pares nome/valor, semelhante a uma cadeia de consulta URI. Este é o formato padrão do POST.
dados de várias partes/formulários Os dados do formulário são codificados como uma mensagem MIME multiparte. Use este formato se estiver a carregar um ficheiro para o servidor.

A Parte 1 deste artigo analisa o formato x-www-form-urlencoded. A Parte 2 descreve o MIME multiparte.

Envio de Tipos Complexos

Normalmente, enviará um tipo complexo, composto por valores retirados de vários controlos de formulário. Considere o seguinte modelo que representa uma atualização de estado:

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 Web API que aceita um Update objeto via 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);
            }
        }

    }
}

Observação

Este controlador usa encaminhamento baseado em ações, pelo que o modelo de rota é "api/{controlador}/{ação}/{id}". O cliente irá publicar os dados em "/api/updates/complex".

Agora vamos escrever um formulário HTML para os utilizadores submeterem uma atualização de estado.

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

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

Captura de ecrã do formulário Complexo Tipo H T M L com um campo de Estado e um campo Data preenchido com valores.

Quando o utilizador clica em Enviar, o navegador envia um pedido HTTP semelhante ao 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

Note que o corpo do pedido contém os dados do formulário, formatados como pares nome/valor. A Web API converte automaticamente os pares nome/valor numa instância da Update classe.

Envio de Dados de Formulário via AJAX

Quando um utilizador submete um formulário, o navegador afasta-se da página atual e apresenta o corpo da mensagem de resposta. Isso é aceitável quando a resposta é uma página HTML. Com uma API web, no entanto, o corpo da resposta está geralmente vazio ou contém dados estruturados, como JSON. Nesse caso, faz mais sentido enviar os dados do formulário através de um pedido AJAX, para que a página possa processar a resposta.

O código seguinte mostra como publicar dados de formulários usando 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 submissão jQuery substitui a ação do formulário por uma nova função. Isto sobrepõe-se ao comportamento padrão do botão Submeter. A função de serializar serializa os dados do formulário em pares nome/valor. Para enviar os dados do formulário para o servidor, chame $.post().

Quando o pedido é concluído, o .success() handler ou .error() apresenta uma mensagem apropriada ao utilizador.

Captura de ecrã do formulário HTML de Tipo Complexo com um erro de host local apresentado em negrito para o utilizador.

Envio de Tipos Simples

Nas secções anteriores, enviámos um tipo complexo, que a Web API desserializou para uma instância de uma classe modelo. Também pode enviar tipos simples, como uma cadeia.

Observação

Antes de enviar um tipo simples, considere envolver o valor num tipo complexo. Isto dá-lhe os benefícios da validação do modelo do lado do servidor e facilita a extensão do seu modelo, se necessário.

Os passos básicos para enviar um tipo simples são os mesmos, mas existem duas diferenças subtis. Primeiro, no controlador, tens de 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 defeito, a Web API tenta obter tipos simples do URI do pedido. O atributo FromBody indica à Web API para ler o valor do corpo da solicitação.

Observação

A Web API lê o corpo da resposta no máximo uma vez, pelo que apenas um parâmetro de uma ação pode vir do corpo do pedido. Se precisares de obter múltiplos valores do corpo do pedido, define um tipo complexo.

Em segundo lugar, o cliente precisa de 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 suportam isto para formulários HTML, mas cria-se este formato em script da seguinte forma:

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

Aqui está 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 submeter o valor do formulário. A única diferença em relação ao guião 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;
});

Pode usar a mesma abordagem para enviar um array de tipos simples:

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

Recursos adicionais

Parte 2: Upload de Ficheiros e MIME Multiparte