application/x-www-form-urlencoded или multipart/form-data?

В HTTP есть два способа POST-данных: application/x-www-form-urlencoded а также multipart/form-data, Я понимаю, что большинство браузеров могут загружать файлы только в том случае, если multipart/form-data используется. Существуют ли какие-либо дополнительные указания, когда следует использовать один из типов кодирования в контексте API (без использования браузера)? Например, это может быть основано на:

  • размер данных
  • существование не-ASCII символов
  • существование на (некодированных) двоичных данных
  • необходимость передавать дополнительные данные (например, имя файла)

В основном я не нашел никаких официальных указаний относительно использования различных типов контента.

7 ответов

Решение

TL;DR

Резюме; если у вас есть двоичные (не алфавитно-цифровые) данные (или полезные данные значительно большего размера) для передачи, используйте multipart/form-data, В противном случае используйте application/x-www-form-urlencoded,


Типы MIME, которые вы упоминаете, - это два Content-Type заголовки для запросов HTTP POST, которые должны поддерживать пользовательские агенты (браузеры). Целью обоих типов запросов является отправка списка пар имя / значение на сервер. В зависимости от типа и объема передаваемых данных один из методов будет более эффективным, чем другой. Чтобы понять почему, вы должны посмотреть на то, что каждый делает под одеялом.

За application/x-www-form-urlencodedтело HTTP-сообщения, отправляемого на сервер, по сути является одной гигантской строкой запроса - пары имя / значение разделяются амперсандом (&), а имена отделяются от значений символом равенства (=). Примером этого может быть:

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

Согласно спецификации:

[Зарезервировано и] не буквенно-цифровые символы заменены на `%HH', знак процента и две шестнадцатеричные цифры, представляющие код символа ASCII

Это означает, что для каждого не алфавитно-цифрового байта, который существует в одном из наших значений, потребуется три байта для его представления. Для больших двоичных файлов утроение полезной нагрузки будет крайне неэффективным.

Это где multipart/form-data с этим методом передачи пар имя / значение каждая пара представляется как "часть" в сообщении MIME (как описано в других ответах). Части разделены определенной границей строки (выбранной специально, чтобы эта строка границы не встречалась ни в одной из полезных нагрузок "value"). Каждая часть имеет свой собственный набор заголовков MIME, таких как Content-Typeи особенно Content-Disposition, который может дать каждой части свое "имя". Часть значения каждой пары имя / значение является полезной нагрузкой каждой части сообщения MIME. Спецификация MIME дает нам больше возможностей при представлении значения полезной нагрузки - мы можем выбрать более эффективное кодирование двоичных данных для экономии полосы пропускания (например, base 64 или даже необработанный двоичный файл).

Почему бы не использовать multipart/form-data все время? Для коротких буквенно-цифровых значений (как и для большинства веб-форм) затраты на добавление всех заголовков MIME значительно перевесят любую экономию от более эффективного двоичного кодирования.

ЧИТАЙТЕ МЕНЬШЕ ПЕРВЫЙ ПАРА ЗДЕСЬ!

Я знаю, что это на 3 года позже, но ответ Мэтта (принятый) неполон и в конечном итоге приведет к неприятностям. Ключевым моментом здесь является то, что, если вы решите использовать multipart/form-data граница не должна появляться в данных файла, которые в итоге получает сервер.

Это не проблема для application/x-www-form-urlencoded потому что нет границы. x-www-form-urlencoded также всегда может обрабатывать двоичные данные, просто превратив один произвольный байт в три 7BIT байт. Неэффективно, но это работает (и обратите внимание, что комментарий о невозможности отправить имена файлов, а также двоичные данные неверен; вы просто отправляете его как другую пару ключ / значение).

Проблема с multipart/form-data является то, что разделитель границ не должен присутствовать в данных файла (см. RFC2388; раздел 5.2 также содержит довольно слабое оправдание отсутствия надлежащего агрегатного MIME-типа, позволяющего избежать этой проблемы).

Итак, на первый взгляд, multipart/form-data не имеет никакого значения при загрузке файлов, двоичных или иных. Если вы не выберете свою границу правильно, то у вас в конечном итоге возникнет проблема, отправляете ли вы простой текст или необработанный двоичный файл - сервер найдет границу не в том месте, и ваш файл будет усечен, или POST не удастся.

Ключ заключается в том, чтобы выбрать кодировку и границу, чтобы выбранные символы границы не могли появиться в закодированном выводе. Одним из простых решений является использование base64 (не используйте сырые двоичные файлы). В base64 3 произвольных байта кодируются в четыре 7-битных символа, где выходной набор символов равен [A-Za-z0-9+/=] (т. е. буквенно-цифровые символы или '+', '/', '='). = это особый случай, и может появляться только в конце закодированного вывода, как один = или двойной ==, Теперь выберите вашу границу в виде 7-битной строки ASCII, которая не может появиться в base64 выход. Многие варианты, которые вы видите в сети, не проходят этот тест - MDN формирует документы, например, использует "blob" в качестве границы при отправке двоичных данных - не очень хорошо. Однако что-то вроде "! Blob!" никогда не появится в base64 выход.

Я не думаю, что HTTP ограничен POST в multipart или x-www-form-urlencoded. Заголовок Content-Type является ортогональным к методу HTTP POST (вы можете заполнить MIME-тип, который вам подходит). Это также относится к типичным веб-приложениям, основанным на представлении HTML (например, полезная нагрузка json стала очень популярной для передачи полезной нагрузки для запросов ajax).

Что касается Restful API через HTTP, наиболее популярными типами контента, с которыми я сталкивался, являются application/xml и application/json.

Приложение / XML:

  • размер данных: XML очень многословен, но обычно не представляет проблемы при использовании сжатия и думает, что случай доступа для записи (например, через POST или PUT) гораздо реже, чем доступ для чтения (во многих случаях он составляет <3% от всего трафика). Редко бывают случаи, когда мне приходилось оптимизировать производительность записи
  • существование не ascii символов: вы можете использовать utf-8 в качестве кодировки в XML
  • существование двоичных данных: необходимо использовать кодировку base64
  • данные имени файла: вы можете инкапсулировать это внутреннее поле в XML

Применение / JSON

  • размер данных: более компактный, чем XML, по-прежнему текст, но вы можете сжать
  • не ascii chars: JSON является UTF-8
  • двоичные данные: base64 (также см. json-binary-question)
  • данные имени файла: инкапсулировать как собственный раздел поля внутри json

двоичные данные как собственный ресурс

Я бы попытался представить двоичные данные как собственный актив / ресурс. Он добавляет еще один вызов, но лучше разбирает вещи. Примеры изображений:

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

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

В последующих ресурсах вы можете просто вставить бинарный ресурс в виде ссылки:

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

Я согласен со многим, что сказал Мануэль. На самом деле, его комментарии относятся к этому URL...

http://www.w3.org/TR/html401/interact/forms.html

... в котором говорится:

Тип содержимого "application/x-www-form-urlencoded" неэффективен для отправки большого количества двоичных данных или текста, содержащего символы не ASCII. Тип контента "multipart/form-data" должен использоваться для отправки форм, которые содержат файлы, данные не ASCII и двоичные данные.

Тем не менее, для меня все сводится к поддержке инструмента / фреймворка.

  • С какими инструментами и средами вы ожидаете, что ваши пользователи API будут создавать свои приложения?
  • Есть ли у них фреймворки или компоненты, которые они могут использовать в пользу одного метода перед другим?

Если вы получите четкое представление о ваших пользователях и о том, как они будут использовать ваш API, то это поможет вам принять решение. Если вы сделаете загрузку файлов трудной для ваших пользователей API, они уйдут, и вы потратите много времени на их поддержку.

Вторичным для этого будет инструмент поддержки, который вы имеете для написания своего API и насколько легко для вас приспособить один механизм загрузки над другим.

Небольшой намек на загрузку данных изображения холста HTML5:

Я работаю над проектом для типографии, и у меня возникли некоторые проблемы из-за загрузки изображений на сервер из HTML5. canvas элемент. Я боролся, по крайней мере, час, и я не получил его, чтобы правильно сохранить изображение на моем сервере.

Однажды я установил contentType вариант моего вызова JQuery AJAX к application/x-www-form-urlencoded все прошло правильно, и данные, закодированные в base64, были правильно интерпретированы и успешно сохранены в виде изображения.


Может быть, это кому-то помогает!

Если вам нужно использовать Content-Type=x-www-urlencoded-form, тогда НЕ используйте FormDataCollection в качестве параметра: в asp.net Core 2+ FormDataCollection не имеет конструкторов по умолчанию, которые требуются для Formatters. Вместо этого используйте IFormCollection:

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

В моем случае проблема заключалась в том, что ответcontentTypeбылapplication/x-www-form-urlencodedно на самом деле он содержалJSONкак тело запроса. Джанго, когда мы получаем доступrequest.dataв Django он не может правильно преобразовать его, поэтому доступrequest.body.

Обратитесь к этому ответу для лучшего понимания:Исключение: вы не можете получить доступ к телу после чтения из потока данных запроса

Другие вопросы по тегам