Envoi de données de formulaire HTML dans API Web ASP.NET : données codées par url de formulaire

Partie 1 : Données codées par url de formulaire

Cet article explique comment publier des données urlencoded form sur un contrôleur d’API web.

Vue d’ensemble des formulaires HTML

Les formulaires HTML utilisent GET ou POST pour envoyer des données au serveur. L’attribut method de l’élément form donne la méthode HTTP :

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

La méthode par défaut est GET. Si le formulaire utilise GET, les données du formulaire sont encodées dans l’URI en tant que chaîne de requête. Si le formulaire utilise POST, les données du formulaire sont placées dans le corps de la demande. Pour les données POSTed, l’attribut enctype spécifie le format du corps de la demande :

enctype Description
application/x-www-form-urlencoded Les données de formulaire sont encodées sous forme de paires nom/valeur, comme une chaîne de requête d’URI. Il s’agit du format par défaut pour POST.
multipart/form-data Les données de formulaire sont encodées sous la forme d’un message MIME en plusieurs parties. Utilisez ce format si vous chargez un fichier sur le serveur.

La partie 1 de cet article examine le format x-www-form-urlencoded. La partie 2 décrit mime en plusieurs parties.

Envoi de types complexes

En règle générale, vous envoyez un type complexe, composé de valeurs extraites de plusieurs contrôles de formulaire. Considérez le modèle suivant qui représente une mise à jour 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; }
    }
}

Voici un contrôleur d’API web qui accepte un Update objet 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);
            }
        }

    }
}

Notes

Ce contrôleur utilise le routage basé sur les actions, de sorte que le modèle d’itinéraire est « api/{controller}/{action}/{id} ». Le client publiera les données sur « /api/updates/complex ».

Nous allons maintenant écrire un formulaire HTML pour que les utilisateurs envoient une mise à jour 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>

Notez que l’attribut d’action sur le formulaire est l’URI de notre action de contrôleur. Voici le formulaire avec certaines valeurs entrées dans :

Capture d’écran du formulaire HT L de type complexe avec un champ État et un champ Date rempli avec des valeurs.

Lorsque l’utilisateur clique sur Envoyer, le navigateur envoie une requête HTTP similaire à ce qui suit :

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

Notez que le corps de la demande contient les données de formulaire, mises en forme sous forme de paires nom/valeur. L’API web convertit automatiquement les paires nom/valeur en instance de la Update classe .

Envoi de données de formulaire via AJAX

Lorsqu’un utilisateur envoie un formulaire, le navigateur s’éloigne de la page active et affiche le corps du message de réponse. C’est correct quand la réponse est une page HTML. Toutefois, avec une API web, le corps de la réponse est généralement vide ou contient des données structurées, telles que JSON. Dans ce cas, il est plus judicieux d’envoyer les données du formulaire à l’aide d’une requête AJAX, afin que la page puisse traiter la réponse.

Le code suivant montre comment publier des données de formulaire à l’aide de 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 fonction d’envoi jQuery remplace l’action de formulaire par une nouvelle fonction. Cela remplace le comportement par défaut du bouton Envoyer. La fonction sérialiser sérialise les données de formulaire en paires nom/valeur. Pour envoyer les données du formulaire au serveur, appelez $.post().

Une fois la demande terminée, le .success() gestionnaire ou .error() affiche un message approprié à l’utilisateur.

Capture d’écran du formulaire HT L de type complexe avec une erreur d’hôte local affichée en gras pour l’utilisateur.

Envoi de types simples

Dans les sections précédentes, nous avons envoyé un type complexe, que l’API Web désérialisée à un instance d’une classe de modèle. Vous pouvez également envoyer des types simples, tels qu’une chaîne.

Notes

Avant d’envoyer un type simple, envisagez plutôt d’encapsuler la valeur dans un type complexe. Cela vous offre les avantages de la validation du modèle côté serveur et facilite l’extension de votre modèle si nécessaire.

Les étapes de base pour envoyer un type simple sont les mêmes, mais il existe deux différences subtiles. Tout d’abord, dans le contrôleur, vous devez décorer le nom du paramètre avec l’attribut 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);
    }

Par défaut, l’API web tente d’obtenir des types simples à partir de l’URI de requête. L’attribut FromBody indique à l’API web de lire la valeur à partir du corps de la requête.

Notes

L’API web lit le corps de la réponse au plus une fois, de sorte qu’un seul paramètre d’une action peut provenir du corps de la requête. Si vous devez obtenir plusieurs valeurs du corps de la demande, définissez un type complexe.

Deuxièmement, le client doit envoyer la valeur au format suivant :

=value

Plus précisément, la partie nom de la paire nom/valeur doit être vide pour un type simple. Tous les navigateurs ne le prennent pas en charge pour les formulaires HTML, mais vous créez ce format dans un script comme suit :

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

Voici un exemple de formulaire :

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

Voici le script permettant d’envoyer la valeur du formulaire. La seule différence par rapport au script précédent est l’argument passé dans la fonction de publication .

$('#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;
});

Vous pouvez utiliser la même approche pour envoyer un tableau de types simples :

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

Ressources supplémentaires

Partie 2 : Chargement de fichiers et MIME en plusieurs parties