Application/x-www-form-urlencoded или multipart/form-data?
В HTTP есть два способа для данных POST: application/x-www-form-urlencoded
и multipart/form-data
. Я понимаю, что большинство браузеров могут загружать файлы только в том случае, если используется multipart/form-data
. Есть ли дополнительные указания, когда использовать один из типов кодирования в контексте API (без участия браузера)? Это может быть, например, основываться на:
- размер данных
- существование символов, отличных от ASCII
- существование на (некодированных) двоичных данных
- необходимость переноса дополнительных данных (например, имя файла)
В основном я не нашел официальных указаний в Интернете относительно использования разных типов контента.
Ответы
Ответ 1
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 значительно перевесят любую экономию от более эффективного двоичного кодирования.
Ответ 2
ЧИТАЙТЕ МЕНЬШЕ ПЕРВЫЙ ПАРА ЗДЕСЬ!
Я знаю, что это на 3 года позже, но ответ мэтта (принятый) является неполным и в конечном итоге приведет к неприятностям. Ключевым моментом здесь является то, что, если вы решите использовать multipart/form-data
, граница не должна появляться в данных файла, которые в итоге получает сервер.
Это не проблема для application/x-www-form-urlencoded
, потому что нет границы. x-www-form-urlencoded
также всегда может обрабатывать двоичные данные, просто превращая один произвольный байт в три 7BIT
байта. Неэффективно, но это работает (и обратите внимание, что комментарий о невозможности отправить имена файлов, а также двоичные данные неверен; вы просто отправляете его как другую пару ключ/значение).
Проблема с multipart/form-data
заключается в том, что в данных файла не должен присутствовать разделитель границ (см. RFC 2388; раздел 5.2 также содержит довольно слабое оправдание отсутствия надлежащего агрегатного MIME-типа, позволяющего избежать этой проблемы).).
Таким образом, на первый взгляд, multipart/form-data
не имеет никакого значения при загрузке файлов, двоичных или иных. Если вы не выберете свою границу правильно, то у вас в конечном итоге возникнет проблема, отправляете ли вы простой текст или необработанный двоичный файл - сервер найдет границу не в том месте, и ваш файл будет усечен, или POST потерпит неудачу.
Ключ должен выбрать кодировку и границу, чтобы выбранные символы границы не могли появиться в закодированном выводе. Одним из простых решений является использование base64
(не используйте необработанный двоичный файл). В base64 3 произвольных байта кодируются в четыре 7-битных символа, где выходной набор символов равен [A-Za-z0-9+/=]
(то есть буквенно-цифровые символы, '+', '/' или '='). =
является особым случаем и может появляться только в конце кодированного выхода, как одиночный =
или двойной ==
. Теперь выберите вашу границу в виде 7-битной строки ASCII, которая не может появиться в выходных данных base64
. Многие варианты, которые вы видите в сети, не проходят этот тест - например, формы MDN docs, используют "blob" в качестве границы при отправке двоичных данных - не очень хорошо. Однако что-то вроде "! Blob!" никогда не появится в выводе base64
.
Ответ 3
Я не думаю, что HTTP ограничен POST в multipart или x-www-form-urlencoded. Заголовок Content-Type Header является ортогональным методу HTTP POST (вы можете заполнить MIME-тип, который вам подходит). Это также относится к типичным веб-приложениям, основанным на представлении HTML (например, полезная нагрузка json стала очень популярной для передачи полезной нагрузки для запросов ajax).
Что касается Restful API over HTTP, наиболее популярными типами контента, с которыми я пришел, являются application/xml и application/json.
приложения /XML:
- размер данных: XML очень многословный, но обычно не является проблемой при использовании сжатия и считается, что случай доступа к записи (например, через POST или PUT) намного реже, чем доступ для чтения (во многих случаях он равен < 3 % всего трафика). Редко там, где мне приходилось оптимизировать производительность записи
- существование символов non-ascii: вы можете использовать utf-8 в качестве кодировки в XML
- существование двоичных данных: нужно будет использовать кодировку base64
- filename data: вы можете инкапсулировать это внутреннее поле в XML
приложения /JSON
- размер данных: компактнее, чем XML, все еще текст, но вы можете сжимать
- non-ascii chars: json is utf-8
- двоичные данные: base64 (также см. json-binary-question)
- filename data: инкапсулировать как собственный полевой раздел внутри 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>
Ответ 4
Я согласен с тем, что сказал Мануэль. Фактически, его комментарии относятся к этому URL...
http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
..., который гласит:
Тип содержимого "application/x-www-form-urlencoded" - это неэффективен для отправки больших количества двоичных данных или текста содержащие символы, отличные от ASCII. тип контента "multipart/form-data" следует использовать для отправки форм которые содержат файлы, данные, отличные от ASCII, и двоичные данные.
Однако для меня это будет связано с поддержкой инструментов/фреймов.
- Какие инструменты и рамки вы используете
ожидайте, что ваши пользователи API будут строить
их приложения?
- Есть ли у них
рамки или компоненты, которые они могут использовать
которые способствуют одному методу
другой?
Если вы четко представляете своих пользователей и как они будут использовать ваш API, то это поможет вам решить. Если вы загрузите файлы для своих пользователей API, они уйдут, и вы потратите много времени на их поддержку.
Вторичным для этого будет поддержка инструмента, которую вы имеете для написания вашего API, и насколько это просто для вашего размещения одного механизма загрузки над другим.
Ответ 5
Немного подсказки с моей стороны для загрузки данных изображения холста HTML5:
Я работаю над проектом для типографии и имел некоторые проблемы из-за загрузки изображений на сервер, который пришел из элемента HTML5 canvas
. Я боролся в течение по крайней мере часа, и я не получил его, чтобы правильно сохранить изображение на моем сервере.
Как только я установил
contentType
вариант моего jQuery ajax-вызова на application/x-www-form-urlencoded
все пошло правильно, и данные, закодированные в base64, были правильно интерпретированы и успешно сохранены как изображение.
Может быть, это помогает кому-то!
Ответ 6
Если вам нужно использовать Content-Type = x-www-urlencoded-form, тогда НЕ используйте FormDataCollection в качестве параметра: В asp.net Core 2+ FormDataCollection не имеет конструкторов по умолчанию, которые требуются для Formatters. Вместо этого используйте IFormCollection:
public IActionResult Search([FromForm]IFormCollection type)
{
return Ok();
}