Wysyłanie danych formularza HTML w internetowym interfejsie API ASP.NET: dane zakodowane w formacie form

Część 1: Dane zakodowane jako Form-urlencoded

W tym artykule pokazano, jak przesyłać dane zakodowane w formacie form-urlencoded do kontrolera interfejsu API sieci Web.

Uwaga / Notatka

Pobierz ukończony projekt.

Omówienie formularzy HTML

Formularze HTML używają metody GET lub POST do wysyłania danych na serwer. Atrybut metody elementu formularza daje metodę HTTP:

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

Domyślną metodą jest GET. Jeśli formularz używa metody GET, dane formularza są kodowane w identyfikatorze URI jako ciąg zapytania. Jeśli formularz używa funkcji POST, dane formularza są umieszczane w treści żądania. W przypadku danych POSTed atrybut entype określa format treści żądania:

typ kodowania Opis
application/x-www-form-urlencoded Dane formularza są kodowane jako pary nazwa/wartość, podobnie jak ciąg zapytania identyfikatora URI. Jest to domyślny format post.
multipart/form-data Dane formularza są kodowane jako wieloczęściowy komunikat MIME. Użyj tego formatu, jeśli przekazujesz plik na serwer.

Część 1 tego artykułu dotyczy formatu x-www-form-urlencoded. Część 2 opisuje wieloczęściowy MIME.

Wysyłanie typów złożonych

Zazwyczaj wysyła się typ złożony składający się z wartości pobranych z kilku kontrolek formularza. Rozważmy następujący model reprezentujący aktualizację stanu:

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

Oto kontroler internetowego interfejsu API, który akceptuje Update obiekt za pośrednictwem funkcji 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);
            }
        }

    }
}

Uwaga / Notatka

Ten kontroler używa routingu opartego na akcjach, więc szablon trasy to "api/{controller}/{action}/{id}". Klient opublikuje dane w pliku "/api/updates/complex".

Teraz napiszmy formularz HTML, aby użytkownicy przesyłali aktualizację stanu.

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

Zwróć uwagę, że atrybut akcji w formularzu jest identyfikatorem URI akcji kontrolera. Oto formularz z pewnymi wartościami wprowadzonymi w:

Zrzut ekranu przedstawiający formularz Typu złożonego H T M L z polem Stan i polem Date wypełnionymi wartościami.

Gdy użytkownik kliknie przycisk Prześlij, przeglądarka wyśle żądanie HTTP podobne do następującego:

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

Zwróć uwagę, że treść żądania zawiera dane formularza sformatowane jako pary nazwa/wartość. Internetowy interfejs API automatycznie konwertuje pary nazw/wartości na wystąpienie Update klasy.

Wysyłanie danych formularza za pośrednictwem technologii AJAX

Gdy użytkownik prześle formularz, przeglądarka odchodzi od bieżącej strony i renderuje treść komunikatu odpowiedzi. Jest to ok, gdy odpowiedź jest stroną HTML. Jednak w przypadku internetowego interfejsu API treść odpowiedzi jest zwykle pusta lub zawiera dane ustrukturyzowane, takie jak JSON. W takim przypadku warto wysłać dane formularza przy użyciu żądania AJAX, aby strona mogła przetworzyć odpowiedź.

Poniższy kod pokazuje, jak publikować dane formularza przy użyciu zapytania 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>

Funkcja jQuery submit zastępuje akcję formularza nową funkcją. Spowoduje to zastąpienie domyślnego zachowania przycisku Prześlij. Funkcja serialize serializuje dane formularza do par nazwa/wartość. Aby wysłać dane formularza do serwera, wywołaj metodę $.post().

Po zakończeniu żądania, program obsługi .success() lub .error() wyświetla odpowiedni komunikat użytkownikowi.

Zrzut ekranu przedstawiający formularz typu złożonego H T M L z błędem hosta lokalnego wyświetlanym pogrubionym tekstem dla użytkownika.

Wysyłanie typów prostych

W poprzednich sekcjach wysłaliśmy typ złożony, który internetowy interfejs API deserializował do wystąpienia klasy modelu. Można również wysyłać proste typy, takie jak ciąg.

Uwaga / Notatka

Przed wysłaniem prostego typu rozważ opakowanie wartości w typ złożony. Zapewnia to korzyści z weryfikacji modelu po stronie serwera i ułatwia rozszerzanie modelu w razie potrzeby.

Podstawowe kroki wysyłania prostego typu danych są takie same, ale istnieją dwie subtelne różnice. Najpierw w kontrolerze należy udekorować nazwę parametru za pomocą atrybutu 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);
    }

Domyślnie Web API próbuje uzyskać proste typy z URI żądania. Atrybut FromBody informuje internetowy interfejs API o odczytaniu wartości z treści żądania.

Uwaga / Notatka

Internetowy interfejs API odczytuje treść odpowiedzi co najwyżej raz, więc tylko jeden parametr akcji może pochodzić z treści żądania. Jeśli musisz pobrać wiele wartości z treści żądania, zdefiniuj typ złożony.

Po drugie, klient musi wysłać wartość w następującym formacie:

=value

W szczególności część nazwy w parze nazwa/wartość musi być pusta dla prostego typu. Nie wszystkie przeglądarki obsługują tę funkcję dla formularzy HTML, ale ten format można utworzyć w skryfcie w następujący sposób:

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

Oto przykładowy formularz:

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

Oto skrypt do przesłania wartości formularza. Jedyną różnicą z poprzedniego skryptu jest argument przekazany do funkcji 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;
});

Możesz użyć tego samego podejścia do wysyłania tablicy prostych typów:

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

Dodatkowe zasoby

Część 2. Przekazywanie plików i wieloczęściowe MIME