web-dev-qa-db-fra.com

application / x-www-form-urlencoded ou multipart / form-data?

Dans HTTP, il y a deux façons de POST données: application/x-www-form-urlencoded et multipart/form-data. Je comprends que la plupart des navigateurs ne peuvent télécharger des fichiers que si multipart/form-data est utilisé. Existe-t-il des instructions supplémentaires pour utiliser l'un des types de codage dans un contexte d'API (aucun navigateur impliqué)? Cela pourrait par exemple se baser sur:

  • taille des données
  • existence de caractères non-ASCII
  • existence sur des données binaires (non codées)
  • la nécessité de transférer des données supplémentaires (comme le nom de fichier)

En gros, je n'ai trouvé aucune indication formelle sur le Web concernant l'utilisation des différents types de contenu.

1240
max

TL; DR

Sommaire; si vous avez des données binaires (non alphanumériques) (ou une charge utile de taille significative) à transmettre, utilisez multipart/form-data. Sinon, utilisez application/x-www-form-urlencoded.


Les types MIME que vous mentionnez sont les deux en-têtes Content-Type pour HTTP POST que les agents d'utilisateurs (navigateurs) doivent prendre en charge. L'objectif de ces deux types de demandes est d'envoyer une liste de paires nom/valeur au serveur. Selon le type et la quantité de données transmises, l’une des méthodes sera plus efficace que l’autre. Pour comprendre pourquoi, il faut regarder ce que chacun fait sous la couverture.

Pour application/x-www-form-urlencoded, le corps du message HTTP envoyé au serveur est essentiellement constitué d’une chaîne de requête géante - les paires nom/valeur sont séparées par la perluète (&), et les noms sont séparés des valeurs par symbole égal (=). Un exemple de ceci serait:

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

Selon le spécification :

Les caractères [réservés et] non alphanumériques sont remplacés par "% HH", un signe de pourcentage et deux chiffres hexadécimaux représentant le code ASCII du caractère.

Cela signifie que pour chaque octet non alphanumérique existant dans l'une de nos valeurs, il faudra trois octets pour le représenter. Pour les gros fichiers binaires, le triplement de la charge utile sera très inefficace.

C'est là que multipart/form-data entre en jeu. Avec cette méthode de transmission de paires nom/valeur, chaque paire est représentée en tant que "partie" dans un message MIME (comme décrit par d'autres réponses). Les parties sont séparées par une limite de chaîne particulière (choisie spécifiquement pour que cette chaîne de limite ne figure pas dans les charges utiles "valeur"). Chaque partie a son propre ensemble d’en-têtes MIME comme Content-Type, et en particulier Content-Disposition, qui peuvent donner à chaque partie son "nom". La partie valeur de chaque paire nom/valeur est la charge utile de chaque partie du message MIME. La spécification MIME nous donne plus d’options pour représenter la valeur de la charge utile - nous pouvons choisir un codage plus efficace des données binaires pour économiser la bande passante (par exemple, base 64 ou même binaire brut).

Pourquoi ne pas utiliser multipart/form-data tout le temps? Pour les valeurs alphanumériques courtes (comme la plupart des formulaires Web), l'ajout de tous les en-têtes MIME aura pour corollaire une réduction considérable des économies réalisées grâce à un codage binaire plus efficace.

1888
Matt Bridges

LIRE AT AU MOINS LE PREMIER PARA ICI!

Je sais que c'est 3 ans trop tard, mais la réponse (acceptée) de Matt est incomplète et finira par vous causer des ennuis. La clé ici est que, si vous choisissez d'utiliser multipart/form-data, la limite doit not apparaît dans les données du fichier que le serveur reçoit finalement.

Ce n'est pas un problème pour application/x-www-form-urlencoded, car il n'y a pas de limite. x-www-form-urlencoded peut également toujours traiter des données binaires, simplement en transformant un octet arbitraire en trois 7BIT octets. Inefficace, mais cela fonctionne (et notez que le commentaire sur l'impossibilité d'envoyer des noms de fichiers ainsi que des données binaires est incorrect; vous l'envoyez simplement comme une autre paire clé/valeur).

Le problème avec multipart/form-data est que le séparateur de limites ne doit pas être présent dans les données du fichier (voir RFC 2388 ; la section 5.2 inclut également une excuse assez fade pour ne pas avoir un type MIME agrégé approprié évite ce problème).

Donc, à première vue, multipart/form-data n'a aucune valeur dans any upload de fichier, binaire ou autre. Si vous ne choisissez pas correctement votre limite, alors vous (= ++ ) == avez éventuellement un problème, que vous envoyiez du texte brut ou du binaire brut - le serveur trouvera une limite incorrecte. place, et votre fichier sera tronqué, ou le POST échouera.

La clé consiste à choisir un codage et une limite tels que les caractères sélectionnés ne puissent pas apparaître dans la sortie codée. Une solution simple consiste à utiliser base64 (do not utilise du binaire brut). Dans base64 , 3 octets arbitraires sont codés en quatre caractères de 7 bits, où le jeu de caractères de sortie est [A-Za-z0-9+/=] (c'est-à-dire des caractères alphanumériques, '+', '/' ou '='). = est un cas spécial et ne peut apparaître qu'à la fin de la sortie codée, sous la forme d'un simple = ou d'un double ==. Choisissez maintenant votre limite en tant que chaîne ASCII de 7 bits qui ne peut pas apparaître dans la sortie base64. De nombreux choix que vous voyez sur le net échouent à ce test - les formulaires MDN docs , par exemple, utilisez "blob" comme limite pour l'envoi de données binaires - ce n'est pas bien. Cependant, quelque chose comme "! Blob!" n'apparaîtra jamais dans la sortie base64.

132
EML

Je ne pense pas que HTTP soit limité à POST en multipart ou x-www-form-urlencoded. Content-Type Header est orthogonal à la méthode HTTP POST (vous pouvez remplir le type MIME qui vous convient). C’est également le cas des applications Web typiques basées sur la représentation HTML (par exemple, la charge utile json est devenue très populaire pour la transmission de la charge utile pour les demandes ajax).

En ce qui concerne Restful API over HTTP, les types de contenu les plus populaires que j'ai rencontrés sont application/xml et application/json.

application/xml:

  • data-size: XML très bavard, mais ce n’est généralement pas un problème lorsque vous utilisez la compression et que vous pensez que le cas d’accès en écriture (par exemple via POST ou PUT) est beaucoup plus rare qu’un accès en lecture (dans de nombreux cas, c’est le cas. <3% de tout le trafic). Rarement là où des cas où je devais optimiser les performances d'écriture
  • existence de caractères non-ascii: vous pouvez utiliser utf-8 comme encodage en XML
  • existence de données binaires: il faudrait utiliser le codage base64
  • nom du fichier: vous pouvez encapsuler ce champ intérieur en XML

application/json

  • data-size: plus compact, moins que XML, texte fixe, mais vous pouvez compresser
  • caractères non-ascii: json est utf-8
  • données binaires: base64 (voir aussi json-binary-question )
  • nom du fichier: encapsule comme une section de champ propre dans json

données binaires comme ressource propre

J'essaierais de représenter les données binaires en tant que ressources/ressources propres. Il ajoute un autre appel mais permet de mieux découpler les choses. Exemple d'images:

POST /images
Content-type: multipart/mixed; boundary="xxxx" 
... multipart data

201 Created
Location: http://imageserver.org/../foo.jpg

Dans les ressources ultérieures, vous pouvez simplement intégrer la ressource binaire en tant que lien:

<main-resource>
 ...
 <link href="http://imageserver.org/../foo.jpg"/>
</main-resource>
87
manuel aldana

Je suis d'accord avec beaucoup de ce que Manuel a dit. En fait, ses commentaires font référence à cette URL ...

http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4

... quels États:

Le type de contenu "application/x-www-form-urlencoded" est inefficace pour l'envoi de grandes quantités de données binaires ou de texte contenant des caractères non-ASCII. Le type de contenu "multipart/form-data" doit être utilisé pour soumettre des formulaires contenant des fichiers, des données non-ASCII et des données binaires.

Cependant, pour moi, cela reviendrait à un support d’outil/cadre.

  • Quels outils et frameworks attendez-vous de vos utilisateurs d'API pour créer leurs applications?
  • Ont-ils des cadres ou des composants qu'ils peuvent utiliser qui favorisent une méthode par rapport à l'autre?

Si vous avez une idée précise de vos utilisateurs et de la manière dont ils utiliseront votre API, cela vous aidera à prendre une décision. Si vous simplifiez le téléchargement de fichiers pour vos utilisateurs d'API, ils s'éloigneront et vous passerez beaucoup de temps à les prendre en charge.

Secondaire, vous disposez de l’aide dont vous disposez pour écrire votre API et de la facilité avec laquelle il est possible d’adapter un mécanisme de téléchargement à un autre.

29
Martin Peck

Juste un petit conseil de mon côté pour le téléchargement de données d’image au format HTML5:

Je travaille sur un projet d'imprimerie et j'ai eu quelques problèmes en raison du téléchargement d'images sur le serveur provenant d'un élément HTML5 canvas. Je me débattais depuis au moins une heure et je ne l’ai pas obtenu pour enregistrer correctement l’image sur mon serveur.

Une fois que j'ai défini l'option contentType de mon appel jQuery ajax sur application/x-www-form-urlencoded, tout s'est déroulé dans le bon sens et les données codées en base64 ont été interprétées correctement et enregistrées avec succès sous forme d'image.


Peut-être que ça aide quelqu'un!

2
Torsten Barthel

Si vous devez utiliser Content-Type = x-www-urlencoded-form, NE PAS utiliser FormDataCollection en tant que paramètre: Dans asp.net Core 2+, FormDataCollection n'a pas de constructeur par défaut requis par Formatters. Utilisez IFormCollection à la place:

 public IActionResult Search([FromForm]IFormCollection type)
    {
        return Ok();
    }
1
jahansha