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

En HTTP hay dos formas de enviar datos por POST: application/x-www-form-urlencoded y multipart/form-data. Tengo entendido que la mayoría de los navegadores sólo son capaces de cargar archivos si se utiliza multipart/form-data. ¿Existe alguna orientación adicional sobre cuándo utilizar uno de los tipos de codificación en un contexto de API (sin navegador)? Esto podría, por ejemplo, basarse en:

  • el tamaño de los datos
  • la existencia de caracteres no ASCII
  • la existencia de datos binarios (no codificados)
  • la necesidad de transferir datos adicionales (como el nombre del archivo)

Básicamente, hasta ahora no he encontrado ninguna orientación formal en la web sobre el uso de los diferentes tipos de contenido.

Solución

TL;DR

Resumen; si tiene datos binarios (no alfanuméricos) (o una carga útil de tamaño significativo) para transmitir, utilice multipart/form-data. En caso contrario, utilice application/x-www-form-urlencoded.


Los tipos MIME que mencionas son las dos cabeceras Content-Type para las peticiones HTTP POST que los agentes de usuario (navegadores) deben soportar. El propósito de estos dos tipos de peticiones es enviar una lista de pares nombre/valor al servidor. Dependiendo del tipo y la cantidad de datos que se transmitan, uno de los métodos será más eficiente que el otro. Para entender por qué, hay que ver lo que hace cada uno bajo cuerda.

En el caso de application/x-www-form-urlencoded, el cuerpo del mensaje HTTP que se envía al servidor es esencialmente una gigantesca cadena de consulta: los pares nombre/valor se separan con el signo de amperio (&), y los nombres se separan de los valores con el símbolo de igualdad (=). Un ejemplo de esto sería: 

MiVariableUna=ValorUna&MiVariableDos=ValorDos

Según la especificación:

\Los caracteres no alfanuméricos [reservados y] se sustituyen por `%HH', un signo de porcentaje y dos dígitos hexadecimales que representan el código ASCII del carácter

Esto significa que por cada byte no alfanumérico que exista en uno de nuestros valores, se necesitarán tres bytes para representarlo. Para los archivos binarios grandes, triplicar la carga útil va a ser muy ineficiente.

Ahí es donde entra multipart/form-data. Con este método de transmisión de pares nombre/valor, cada par se representa como una "parte" en un mensaje MIME (como se describe en otras respuestas). Las partes están separadas por una cadena límite particular (elegida específicamente para que esta cadena límite no aparezca en ninguna de las cargas útiles de "valor"). Cada parte tiene su propio conjunto de cabeceras MIME como Content-Type, y particularmente Content-Disposition, que pueden dar a cada parte su "nombre". La pieza de valor de cada par nombre/valor es la carga útil de cada parte del mensaje MIME. La especificación MIME nos da más opciones a la hora de representar la carga útil del valor: podemos elegir una codificación más eficiente de los datos binarios para ahorrar ancho de banda (por ejemplo, base 64 o incluso binario crudo).

¿Por qué no usar multipart/form-data todo el tiempo? Para valores alfanuméricos cortos (como la mayoría de los formularios web), la sobrecarga de añadir todas las cabeceras MIME va a superar significativamente cualquier ahorro de una codificación binaria más eficiente.

Comentarios (16)

No creo que HTTP esté limitado a POST en multipartes o x-www-form-urlencoded. El [Encabezado de Tipo de Contenido][1] es ortogonal al método HTTP POST (puedes rellenar el tipo MIME que te convenga). Este es también el caso de las típicas aplicaciones web basadas en la representación HTML (por ejemplo, la carga útil json se hizo muy popular para transmitir la carga útil de las peticiones ajax).

En lo que respecta a Restful API sobre HTTP, los tipos de contenido más populares con los que he entrado en contacto son application/xml y application/json.

application/xml:

  • tamaño de los datos: XML muy verboso, pero no suele ser un problema si se utiliza la compresión y se piensa que el caso de acceso de escritura (por ejemplo, a través de POST o PUT) es mucho más raro que el de acceso de lectura (en muchos casos es
Comentarios (3)

Estoy de acuerdo con mucho de lo que ha dicho Manuel. De hecho, sus comentarios se refieren a esta url...

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

... que dice:

El tipo de contenido "application/x-www-form-urlencoded" es ineficiente para enviar grandes cantidades de datos binarios o texto que contenga caracteres no ASCII. El tipo de contenido "multipart/form-data" debe utilizarse para el envío de formularios que contengan archivos, datos no ASCII y datos binarios.

Sin embargo, para mí se reduciría al soporte de la herramienta/marco.

  • ¿Qué herramientas y marcos de trabajo espera que los usuarios de su API construyan sus aplicaciones?
  • ¿Disponen de marcos o componentes que puedan utilizar que favorecen un método sobre el otro? otro?

Si tienes una idea clara de tus usuarios y de cómo van a utilizar tu API, eso te ayudará a decidir. Si haces que la carga de archivos sea difícil para tus usuarios de la API, se alejarán, y tú gastarás mucho tiempo en darles soporte.

En segundo lugar, está el soporte de la herramienta que TÚ tienes para escribir tu API y lo fácil que es para ti acomodar un mecanismo de carga sobre el otro.

Comentarios (2)