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

dans HTTP il y a deux façons de poster des 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é. Y a-t-il d'autres indications sur l'utilisation de l'un des types d'encodage dans le contexte D'une API (pas de navigateur impliqué)? Cela pourrait par exemple être basé sur:

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

en gros, j'ai trouvé aucune directive officielle sur le web concernant l'utilisation des différents types de contenu jusqu'à présent.

1095
demandé sur max 2010-10-24 15:12:01

6 réponses

TL; DR

résumé; 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 les requêtes HTTP POST que les agents utilisateurs (navigateurs) doivent prendre en charge. Le but de ces deux types de requêtes est d'envoyer une liste de paires nom/valeur à serveur. Selon le type et la quantité de données transmises, l'une des méthodes plus efficaces que les autres. Pour comprendre pourquoi, vous devez regarder ce que chacun fait sous les couvertures.

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'ampersand ( & ), et les noms sont séparés des valeurs par le symbole égal ( = ). Un exemple de ce serait:

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

selon la spécification :

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

cela signifie que pour chaque octet non alphanumérique qui existe dans l'une de nos valeurs, il va falloir 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 entre en jeu. 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 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 se produise dans aucune des charges utiles "value"). Chaque partie a son propre jeu de MIME en-têtes comme Content-Type , et particulièrement Content-Disposition , qui peut donner à chaque partie son " nom."La pièce de 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 lorsque nous représentons la charge utile de valeur -- nous pouvons choisir un encodage plus efficace des données binaires pour économiser de 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 formes web), le les frais généraux liés à l'ajout de tous les en-têtes MIME vont dépasser de manière significative les économies réalisées grâce à un encodage binaire plus efficace.

1710
répondu Matt Bridges 2018-02-28 22:42:45

LISEZ AU MOINS LE PREMIER PARAGRAPHE ICI!

je sais que c'est 3 ans trop tard, mais la réponse de Matt (acceptée) 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 et non apparaître dans les données de fichier que le serveur reçoit éventuellement.

ce n'est pas un problème pour application/x-www-form-urlencoded , parce qu'il n'y a pas de limite. x-www-form-urlencoded peut aussi toujours traiter des données binaires, par le simple expédient de transformer un octet arbitraire en trois octets 7BIT . Inefficace, mais cela fonctionne (et notez que le commentaire sur le fait de ne pas pouvoir 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 Frontière ne doit pas être présent dans les données de fichier (voir RFC2388 ; section 5.2 également comprend 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 téléchargement de fichier, binaire ou autre. Si vous ne choisissez pas votre limite correctement, alors vous aura éventuellement un problème, si vous envoyez du texte brut ou binaire - le serveur trouvera une limite au mauvais endroit, et votre fichier sera tronqué, ou le poteau va échouer.

la clé est de choisir un encodage et une limite de telle sorte que les caractères limites sélectionnés ne puissent pas apparaître dans la sortie encodée. Une solution simple est d'utiliser base64 (do pas utiliser binaire brut). Dans base64 3 octets arbitraires sont encodés en quatre caractères de 7 bits, où le jeu de caractères de sortie est [A-Za-z0-9+/=] (c.-à-d. caractères alphanumériques, ou '+', '/', '='). = est un spécial cas, et ne peut apparaître qu'à la fin de la sortie encodée, comme un simple = ou un double == . Maintenant, choisissez votre limite comme chaîne ASCII 7 bits qui ne peut pas apparaître dans la sortie base64 . Beaucoup de choix que vous voyez sur le net échouer ce test - les formulaires MDN docs , par exemple, utiliser "blob" comme une limite lors de l'envoi de données binaires - pas bon. Cependant, quelque chose comme "!blob!"n'apparaîtra jamais en sortie base64 .

112
répondu EML 2014-04-18 11:08:05

Je ne pense pas que HTTP soit limité à publier en multipart ou x-www-form-urlencoded. L'en-tête Content-Type est orthogonale à la méthode HTTP POST (vous pouvez remplir le type MIME qui vous convient). C'est également le cas pour les applications Web de représentation HTML typiques (par exemple, JSON payload est devenu très populaire pour transmettre la charge utile pour les requêtes ajax).

concernant Restful API sur HTTP les types de contenu les plus populaires avec lesquels je suis entré en contact sont application / xml et application/json.

application / xml:

  • taille des données: XML très verbeux, mais généralement pas un problème lors de l'utilisation de la compression et de penser que le cas d'accès en écriture (par exemple par la poste ou PUT) est beaucoup plus rare que l'accès en lecture (dans de nombreux cas, il est <3% de tout le trafic). Rarement là où les cas où j'ai dû optimiser la performance d'écriture
  • existence de caractères non-ascii: vous pouvez utiliser utf-8 comme encodage en XML
  • l'existence de données binaires: utiliser l'encodage base64
  • données Nom du fichier: vous pouvez encapsuler ce champ intérieur en XML

application / json

  • data-size: plus compact moins que XML, encore du texte, mais vous pouvez compresser
  • non-ascii caractères: json est utf-8
  • données binaires: base64 (voir aussi json-binaire-question )
  • données de nom de fichier: encapsulé comme propre zone-section à l'intérieur de json

données binaires en tant que ressource propre

je voudrais essayer de représenter des données binaires comme propre actif/ressource. Il ajoute un autre appel, mais découple les choses mieux. Exemples d'images:

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

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

dans les ressources suivantes, vous pouvez simplement en ligne la ressource binaire comme lien:

<main-resource&gt
 ...
 <link href="http://imageserver.org/../foo.jpg"/>
</main-resource>
82
répondu manuel aldana 2017-05-23 12:10:47

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

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

Cependant, pour moi, cela reviendrait à soutenir l'outil/cadre.

  • Quels sont les outils et les cadres ne vous attendez-vous à ce que vos utilisateurs D'API construisent leurs applications?
  • ont - ils cadres ou composants qu'ils peuvent utiliser qui préfèrent une méthode à la les autres?

si vous avez une idée précise de vos utilisateurs, et de la façon dont ils utiliseront votre API, alors cela vous aidera à décider. Si vous rendez le téléchargement de fichiers difficile pour les utilisateurs de votre API, ils s'en iront, et vous passerez beaucoup de temps à les prendre en charge.

secondaire à cela serait l'outil de soutien que vous avez pour écrire votre API et comment il est facile pour vous d'accommoder un mécanisme de téléchargement sur l'autre.

26
répondu Martin Peck 2012-05-03 14:53:57

juste un petit conseil de mon côté pour télécharger des données image HTML5 canvas:

je travaille sur un projet pour un atelier d'impression et j'ai eu quelques problèmes à cause du téléchargement d'images sur le serveur provenant d'un élément HTML5 canvas . J'ai lutté pendant au moins une heure et je ne l'ai pas obtenu pour sauver l'image correctement sur mon serveur.

une fois que j'ai mis contentType option de mon appel ajax jQuery à application/x-www-form-urlencoded tout s'est bien passé et les données codées par base64 ont été interprétées correctement et sauvegardées avec succès en tant qu'image.


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

0
répondu Torsten Barthel 2016-12-22 10:10:02

si vous devez utiliser Content-Type=x-www-urlencoded-form, alors N'utilisez pas FormDataCollection comme paramètre: In asp.net Core 2+ FormDataCollection n'a aucun constructeur par défaut qui est requis par les formateurs. Utilisez IFormCollection à la place:

 public IActionResult Search([FromForm]IFormCollection type)
    {
        return Ok();
    }
0
répondu jahansha 2018-10-02 18:52:24