Condividi tramite


Invio di dati del modulo HTML nell'API Web di ASP.NET: dati form-urlencoded

Parte 1: Dati form-urlencoded

Questo articolo illustra come inviare dati form-urlencoded a un controller API Web.

Panoramica dei moduli HTML

I moduli HTML usano GET o POST per inviare dati al server. L'attributo del metodo dell'elemento modulo fornisce il metodo HTTP:

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

Il metodo predefinito è GET. Se il modulo usa GET, i dati del modulo vengono codificati nell'URI come stringa di query. Se il modulo utilizza POST, i dati del modulo vengono inseriti nel corpo della richiesta. Per i dati POSTed, l'attributo enctype specifica il formato del corpo della richiesta:

enctype Descrizione
application/x-www-form-urlencoded I dati del modulo vengono codificati come coppie nome/valore, simili a una stringa di query URI. Questo è il formato predefinito per POST.
multipart/form-data I dati del modulo vengono codificati come messaggio MIME multiparte. Usare questo formato se si carica un file nel server.

La parte 1 di questo articolo esamina il formato x-www-form-urlencoded. La parte 2 descrive MIME multiparte.

Invio di tipi complessi

In genere, si invierà un tipo complesso, composto da valori tratti da diversi controlli del modulo. Si consideri il modello seguente che rappresenta un aggiornamento dello stato:

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

Ecco un controller API Web che accetta un Update oggetto tramite 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);
            }
        }

    }
}

Annotazioni

Questo controller usa il routing basato su azioni, quindi il modello di route è "api/{controller}/{action}/{id}". Il client pubblicherà i dati in "/api/updates/complex".

Scrivere ora un modulo HTML per consentire agli utenti di inviare un aggiornamento dello stato.

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

Si noti che l'attributo action nel form è l'URI dell'azione del controller. Ecco il modulo con alcuni valori immessi in:

Screenshot del modulo Complex Type HTML con un campo Stato e un campo Data, entrambi compilati con dei valori.

Quando l'utente fa clic su Invia, il browser invia una richiesta HTTP simile alla seguente:

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

Si noti che il corpo della richiesta contiene i dati del modulo, formattati come coppie nome/valore. L'API Web converte automaticamente le coppie nome/valore in un'istanza della Update classe .

Invio di dati del modulo tramite AJAX

Quando un utente invia un modulo, il browser si allontana dalla pagina corrente ed esegue il rendering del corpo del messaggio di risposta. Ok quando la risposta è una pagina HTML. Con un'API Web, tuttavia, il corpo della risposta è in genere vuoto o contiene dati strutturati, ad esempio JSON. In tal caso, è più opportuno inviare i dati del modulo usando una richiesta AJAX, in modo che la pagina possa elaborare la risposta.

Nel codice seguente viene illustrato come pubblicare i dati del modulo 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>

La funzione di invio di jQuery sostituisce l'azione del modulo con una nuova funzione. In questo modo viene eseguito l'override del comportamento predefinito del pulsante Invia. La funzione serialize serializza i dati del modulo in coppie nome/valore. Per inviare i dati del modulo al server, chiamare $.post().

Al termine della richiesta, il gestore degli eventi .success() o .error() visualizza un messaggio appropriato per l'utente.

Screenshot del modulo Complex Type H T M L con un errore host locale visualizzato in grassetto per l'utente.

Invio di tipi di dati semplici

Nelle sezioni precedenti è stato inviato un tipo complesso, deserializzato dall'API Web a un'istanza di una classe modello. È anche possibile inviare tipi semplici, ad esempio una stringa.

Annotazioni

Prima di inviare un tipo semplice, è consigliabile invece eseguire il wrapping del valore in un tipo complesso. Ciò offre i vantaggi della convalida del modello sul lato server e semplifica l'estensione del modello, se necessario.

I passaggi di base per inviare un tipo semplice sono gli stessi, ma esistono due piccole differenze. Innanzitutto, nel controller è necessario decorare il nome del parametro con l'attributo 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);
    }

Per impostazione predefinita, l'API Web tenta di ottenere tipi semplici dall'URI della richiesta. L'attributo FromBody indica all'API Web di leggere il valore dal corpo della richiesta.

Annotazioni

L'API Web legge il corpo della risposta al massimo una volta, quindi solo un parametro di un'azione può provenire dal corpo della richiesta. Se è necessario ottenere più valori dal corpo della richiesta, definire un tipo complesso.

In secondo luogo, il client deve inviare il valore con il formato seguente:

=value

In particolare, la parte del nome della coppia nome/valore deve essere vuota per un tipo semplice. Non tutti i browser lo supportano per i moduli HTML, ma si crea questo formato nello script come indicato di seguito:

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

Ecco un esempio di modulo:

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

Ed ecco lo script per inviare il valore del modulo. L'unica differenza rispetto allo script precedente è l'argomento passato nella funzione 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;
});

È possibile usare lo stesso approccio per inviare una matrice di tipi semplici:

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

Risorse aggiuntive

Parte 2: Caricamento di file e MIME multipart