1472 votes

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

Dans le protocole HTTP, il existe deux façons de transférer des données : application/x-www-form-urlencoded y multipart/form-data . Je comprends que la plupart des navigateurs ne sont capables de télécharger des fichiers que si multipart/form-data est utilisé. Existe-t-il des indications supplémentaires concernant l'utilisation d'un des types d'encodage dans un contexte d'API (sans navigateur impliqué) ? Cela pourrait, par exemple, être basé 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 du fichier)

Jusqu'à présent, je n'ai trouvé aucune orientation formelle sur le web concernant l'utilisation des différents types de contenu.

90 votes

Il convient de préciser que ce sont les deux types MIME utilisés par les formulaires HTML. HTTP lui-même n'a aucune limitation de ce type... on peut utiliser le type MIME que l'on veut via HTTP.

2182voto

Matt Bridges Points 14547

TL;DR

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


Les types MIME que vous mentionnez sont les deux Content-Type pour les demandes HTTP POST que les agents utilisateurs (navigateurs) doivent prendre en charge. L'objectif de ces deux types de requêtes 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 examiner ce que chacune d'entre elles fait sous le manteau.

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

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

Selon le spécification :

Les caractères non alphanumériques [réservés et] 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, tripler la charge utile sera très inefficace.

C'est là que multipart/form-data vient dans. Avec cette méthode de transmission des paires nom/valeur, chaque paire est représentée comme une "partie" dans un message MIME (comme décrit dans d'autres réponses). Les parties sont séparées par une chaîne de caractères particulière (choisie spécifiquement pour que cette chaîne de caractères ne se retrouve dans aucune des charges utiles "valeur"). Chaque partie a son propre ensemble d'en-têtes MIME comme Content-Type et notamment Content-Disposition qui peut 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 les données utiles : 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), les frais généraux liés à l'ajout de tous les en-têtes MIME vont largement dépasser les économies réalisées grâce à un codage binaire plus efficace.

91 votes

Le format x-www-form-urlencoded a-t-il une limite de longueur, ou est-il illimité ?

0 votes

Base64 peut être plus efficace que le codage URL, mais le binaire direct est le plus efficace.

42 votes

@Pacerier La limite est appliquée par le serveur qui reçoit la demande POST. Voir ce fil de discussion pour plus d'informations : stackoverflow.com/questions/2364840/

175voto

EML Points 1877

LISEZ AU MOINS LE PREMIER PARAGRAPHE 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 attirer des ennuis. La clé ici est que, si vous choisissez d'utiliser multipart/form-data la frontière doit no apparaissent 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 parce qu'il n'y a pas de frontière. x-www-form-urlencoded peut aussi toujours traiter des données binaires, en transformant simplement un octet arbitraire en trois 7BIT octets. C'est inefficace, mais cela fonctionne (et notez que le commentaire concernant l'impossibilité d'envoyer des noms de fichiers ainsi que des données binaires est incorrect ; vous les envoyez simplement comme une autre paire clé/valeur).

Le problème avec multipart/form-data est que le séparateur de frontières ne doit pas être présent dans les données du fichier (cf. RFC 2388 ; la section 5.2 comprend également une excuse plutôt boiteuse pour ne pas avoir un type MIME agrégé approprié qui évite ce problème).

Donc, à première vue, multipart/form-data n'a aucune valeur dans tout le téléchargement de fichiers, binaires ou autres. Si vous ne choisissez pas correctement votre frontière, alors vous sera Le serveur trouvera une limite au mauvais endroit, et votre fichier sera tronqué, ou le POST échouera.

La clé est de choisir un encodage et une frontière tels que les caractères de frontière que vous avez sélectionnés ne puissent pas apparaître dans la sortie encodée. Une solution simple consiste à utiliser base64 (faire no utiliser le binaire brut). Sur base64 3 octets arbitraires sont codés en quatre caractères de 7 bits, le jeu de caractères de sortie étant le suivant [A-Za-z0-9+/=] (c'est-à-dire les caractères alphanumériques, '+', '/' ou '='). = est un cas particulier, et ne peut apparaître qu'à la fin de la sortie codée, sous la forme d'un seul fichier = ou un double == . Maintenant, choisissez votre limite comme une chaîne ASCII de 7 bits qui ne peut pas apparaître dans base64 sortie. De nombreux choix que vous voyez sur le net échouent à ce test - les formulaires MDN docs Par exemple, l'utilisation de "blob" comme limite pour l'envoi de données binaires n'est pas une bonne chose. Cependant, quelque chose comme "!blob !" n'apparaîtra jamais dans les fichiers base64 sortie.

55 votes

Bien qu'une considération de multipart/form-data soit de s'assurer que la limite n'apparaisse pas dans les données, ceci est assez simple à accomplir en choisissant une limite qui est suffisamment longue. N'utilisez pas le codage base64 pour y parvenir. Une limite générée aléatoirement et de même longueur qu'un UUID devrait suffire : stackoverflow.com/questions/1705008/ .

2 votes

Mieux vaut tard que jamais.

1 votes

Cela clarifie le concept lorsqu'il est combiné avec la réponse de Matt approprié ! kudos !

101voto

manuel aldana Points 4317

Je ne pense pas que HTTP soit limité à POST en multipart ou x-www-form-urlencoded. Le site En-tête Content-Type est orthogonal à la méthode HTTP POST (vous pouvez remplir le type MIME qui vous convient). C'est également le cas pour les applications web typiques basées sur la représentation HTML (par exemple, la charge utile json est devenue très populaire pour transmettre les données utiles des requêtes ajax).

En ce qui concerne l'API reposante sur 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 verbeux, mais généralement pas un problème lorsqu'on utilise la compression et qu'on pense que le cas d'accès en écriture (par exemple par POST ou PUT) est beaucoup plus rare que l'accès en lecture (dans de nombreux cas, il représente <3% de tout le trafic). Il y a rarement eu des cas où j'ai dû optimiser les performances en écriture.
  • existence de caractères non ascii : vous pouvez utiliser l'utf-8 comme encodage dans le XML
  • existence de données binaires : il faudrait utiliser l'encodage base64
  • données du nom de fichier : vous pouvez les encapsuler dans un champ XML.

application/json

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

données binaires comme ressource propre

J'essaierais de représenter les données binaires comme un actif/une ressource propre. Cela ajoute un autre appel mais découple mieux les choses. Exemples 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 mettre en ligne la ressource binaire en tant que lien :

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

0 votes

Intéressant. Mais quand utiliser application/x-www-form-urlencoded et quand multipart/form-data ?

4 votes

Application/x-www-form-urlencoded est le mime-type par défaut de votre demande (voir aussi w3.org/TR/html401/interact/forms.html#h-17.13.4 ). Je l'utilise pour les formulaires web "normaux". Pour les API, j'utilise application/xml|json. multipart/form-data est une cloche pour penser aux pièces jointes (dans le corps de la réponse, plusieurs sections de données sont concaténées avec une chaîne de délimitation définie).

6 votes

Je pense que le PO ne demandait probablement que les deux types de formulaires HTML utilisés, mais je suis heureux que cela ait été souligné.

29voto

Martin Peck Points 8078

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

... qui déclare :

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

Toutefois, pour moi, cela se résume à la prise en charge des outils et des cadres.

  • Quels outils et cadres de travail que les utilisateurs de votre API construisent leurs applications ?
  • Ont-ils des cadres ou des composants qu'ils peuvent utiliser qui favorisent une méthode plutôt que 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 rendez le téléchargement de fichiers difficile pour les utilisateurs de votre API, ils s'en iront et vous devrez passer beaucoup de temps à les aider.

En outre, il faut tenir compte de l'outil dont VOUS disposez pour écrire votre API et de la facilité avec laquelle vous pouvez adapter un mécanisme de téléchargement à un autre.

1 votes

Bonjour, est-ce que cela signifie que chaque fois que nous envoyons quelque chose au serveur web, nous devons mentionner quel est le Content-type afin que le serveur web sache s'il doit décoder les données ? Même si nous créons nous-mêmes la requête http, nous devons mentionner le Content-type, n'est-ce pas ?

2 votes

@GMsoF, C'est facultatif. Voir stackoverflow.com/a/16693884/632951 . Vous pouvez éviter d'utiliser content-type lors de l'élaboration d'une requête spécifique pour un serveur spécifique afin d'éviter les surcharges génériques.

2voto

Haibarbe-Software Points 121

Juste une petite astuce de ma part pour télécharger des données d'images HTML5 canvas :

Je travaille sur un projet pour une imprimerie et j'ai rencontré des problèmes liés au téléchargement sur le serveur d'images provenant d'un fichier HTML5. canvas élément. J'ai lutté pendant au moins une heure et je n'ai pas réussi à enregistrer l'image correctement sur mon serveur.

Une fois que j'ai réglé le contentType de mon appel jQuery ajax pour application/x-www-form-urlencoded tout s'est déroulé comme prévu et les données codées en base64 ont été interprétées correctement et enregistrées avec succès en tant qu'image.


Peut-être que cela aidera quelqu'un !

5 votes

Quel type de contenu envoyait-il avant que vous le changiez ? Ce problème peut être dû au fait que le serveur ne prend pas en charge le type de contenu que vous envoyez.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X