urlencoded vs multipart/form-data в API: когда использовать
Сравнение application/x-www-form-urlencoded и multipart/form-data для API post запросов. Рекомендации по размеру данных, не-ASCII символам, двоичным данным и метаданным. Примеры curl, fetch, Python requests.
Когда использовать application/x-www-form-urlencoded vs multipart/form-data в разработке API?
В HTTP существуют два основных типа содержимого для POST-данных: application/x-www-form-urlencoded и multipart/form-data. Хотя я понимаю, что браузеры обычно требуют multipart/form-data для загрузки файлов, мне нужна рекомендация по выбору каждого типа кодирования в контексте API (без участия браузеров).
Можете ли вы предоставить рекомендации на основе следующих факторов:
- Рассмотрения размера данных
- Обработка не-ASCII символов
- Передача двоичных данных
- Необходимость в дополнительных метаданных (например, имен файлов)
Я не нашел исчерпывающих формальных рекомендаций по соответствующим случаям использования этих различных типов содержимого в разработке API.
В API post запросах выбор между application/x-www-form-urlencoded и multipart/form-data определяется типом и объёмом данных: для простых пар «ключ‑значение» и небольшого текстового контента (включая UTF‑8) обычно выгоднее application/x-www-form-urlencoded, а для файлов, больших двоичных payload и когда нужны метаданные (имя файла, Content-Type) — multipart/form-data. Учитывайте, что percent‑кодирование увеличивает размер для не‑ASCII и бинарных данных, а multipart добавляет MIME‑заголовки — нужно сопоставить накладные расходы и удобство обработки. Если данные структурированные (JSON) — чаще проще отправлять application/json, а не форму.
Содержание
- Когда использовать application/x-www-form-urlencoded
- Когда использовать multipart/form-data
- Сравнение по факторам: размер, не‑ASCII, двоичные данные, метаданные
- Размер данных
- Не‑ASCII символы и кодировка
- Двоичные данные
- Метаданные и имена файлов
- Примеры: curl, fetch, Python requests
- Практические советы и распространённые ошибки
- Источники
- Заключение
Когда использовать application/x-www-form-urlencoded
Коротко: когда клиент отправляет только текстовые пары «ключ=значение», без файлов и без необходимости хранить per‑part метаданные — выбирайте application/x-www-form-urlencoded. Формат выглядит как query‑string: name1=value1&name2=value2; все «особые» байты кодируются в %HH (percent‑encoding). Это простой, широко совместимый вариант для POST‑форм и API, которые ожидают form‑encoded данные.
Когда это удобно
- Небольшие формы и параметры (логин, токены, короткие поля).
- Много маленьких полей, где накладные MIME‑заголовки будут ощутимее, чем единственная строка.
- Требуется совместимость с устаревшими интеграциями/сервисами, которые принимают только form‑encoded.
О чём помнить
- Percent‑кодирование применимо и к UTF‑8: сначала текст кодируется в байты UTF‑8, затем байты превращаются в %HH. Это работает, но увеличивает длину тела (особенно для кириллицы и других много‑байтовых наборов).
- Для явного указания кодировки можно использовать заголовок Content‑Type: application/x-www-form-urlencoded; charset=UTF-8 — но поддержку параметра charset лучше проверять на стороне сервера/фреймворка.
- Если API ожидает структурированные данные (сложные JSON‑объекты), application/json обычно удобнее и эффективнее.
Источник по синтаксису и примерам POST — см. документацию MDN: https://developer.mozilla.org/ru/docs/Web/HTTP/Methods/POST и краткое сравнение форматов в примере‑gist: https://gist.github.com/joyrexus/524c7e811e4abf9afe56.
Когда использовать multipart/form-data
Коротко: когда нужно передать файлы или другие двоичные данные, либо каждую часть нужно снабдить собственными заголовками/метаданными — используйте multipart/form-data. Формат разбивает тело на части, разделённые boundary; каждая часть имеет свои HTTP‑заголовки (например, Content-Disposition и Content-Type).
Когда это удобно
- Загрузка файлов (изображения, архивы, двоичные blobs).
- Сценарии, где поле должно иметь своё имя файла (filename) и/или Content‑Type.
- Когда нужен стриминг больших файлов (сервер может обрабатывать части по мере прихода).
О чём помнить
- В заголовке Content‑Type указывается boundary (Content-Type: multipart/form-data; boundary=----XYZ). Клиентские библиотеки (curl -F, формы браузера, библиотеки multipart) обычно генерируют безопасный случайный boundary.
- Для двоичных данных multipart избегает %‑кодирования — это делает его эффективным для больших файлов. Но у него есть MIME‑накладные на каждую часть, поэтому для множества мелких полей это может быть менее выгодно.
- Кодировка файловых имён и текстовых частей иногда вызывает нюансы; используйте UTF‑8 и проверяйте поддержку на клиенте/сервере.
Технические детали и разъяснения формата — см. статью на Wikipedia: https://ru.wikipedia.org/wiki/Multipart/form-data и обзор структуры на Habr: https://habr.com/ru/articles/511114/.
Сравнение по факторам: размер, не‑ASCII, двоичные данные, метаданные
Размер данных
- application/x-www-form-urlencoded: хорош для небольших и средних текстовых наборов. Percent‑кодирование добавляет накладные байты, особенно для нелатиницы и бинарных данных (каждый байт превращается в %HH).
- multipart/form-data: добавляет MIME‑заголовки для каждой части (Content‑Disposition, Content‑Type и др.). Для одного большого файла общая накладная часть мала по сравнению с увеличением, которое дал бы urlencoding бинарного файла; для множества мелких полей — overhead может быть заметен.
Вывод: много мелких текстовых полей → чаще urlencoded; большие файлы/бинар → multipart.
Не‑ASCII символы и кодировка
- В urlencoded: текст сначала кодируется в байты (обычно UTF‑8), затем байты percent‑кодуются. Работает, но тело увеличивается; для больших объёмов текста лучше использовать application/json или multipart с явным charset.
- В multipart: каждая часть может иметь свой заголовок Content‑Type, например text/plain; charset=UTF-8 — то есть можно посылать UTF‑8 напрямую в теле части без percent‑кодирования.
Итого: оба варианта поддерживают UTF‑8, но multipart даёт более явный контроль над кодировкой в каждой части.
Двоичные данные
- urlencoded: возможна передача бинарных данных (через percent‑encoding или base64), но это крайне неэффективно.
- multipart: предназначен для двоичных частей — файл передаётся «как есть», с указанием Content‑Type. Для больших бинарных файлов multipart почти всегда предпочтительнее.
Если по каким‑то причинам multipart нельзя, возможна альтернатива: base64 в JSON/филдe, но размер вырастет примерно на ~33% и теряется потоковая обработка; обычно это компромисс хуже, чем multipart.
Метаданные и имена файлов
- urlencoded: нет механизма передачи per‑file metadata (имя файла, mime‑тип) как части тела — можно передать дополнительные поля с именем строки, но это не заменит стандартных заголовков.
- multipart: стандартно поддерживает Content‑Disposition с параметром filename и Content‑Type для каждой части, что делает его естественным выбором при необходимости передавать метаданные вместе с двоичными данными.
Учтите нюансы кодирования имён файлов в заголовках и совместимость разных клиентов/серверов — библиотеки обычно решают это за вас.
Примеры: curl, fetch, Python requests
Ниже — минимальные шаблоны для клиента (без браузера).
application/x-www-form-urlencoded (curl):
curl -X POST "https://api.example.com/endpoint" \
-H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
--data-urlencode "username=Иван" \
--data-urlencode "message=Привет, мир!"
application/x-www-form-urlencoded (fetch, Node/JS):
const params = new URLSearchParams();
params.append('username', 'Иван');
params.append('message', 'Привет, мир!');
fetch('https://api.example.com/endpoint', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: params.toString()
});
multipart/form-data (curl — файл + поля):
curl -X POST "https://api.example.com/upload" \
-F "file=@./photo.jpg;type=image/jpeg" \
-F "title=Фото с отпуска"
(-F автоматически формирует Content-Type: multipart/form-data; boundary=…)
multipart/form-data (Python requests):
import requests
files = {'file': ('photo.jpg', open('photo.jpg', 'rb'), 'image/jpeg')}
data = {'title': 'Фото с отпуска'}
resp = requests.post('https://api.example.com/upload', files=files, data=data)
Если нужно отправить JSON‑метаданные + файл — проще сделать multipart, где одна часть имеет Content-Type: application/json (или положить JSON как отдельное поле).
Полезная шпаргалка и сравнение режимов в Postman/клиентах — см. обсуждение на StackOverflow: https://stackoverflow.com/questions/26723467/postman-chrome-what-is-the-difference-between-form-data-x-www-form-urlencoded.
Практические советы и распространённые ошибки
- Если API предназначен для структурированных данных — отдавайте предпочтение application/json. Form‑encoding и multipart нужны для совместимости и/или файлов. (Обсуждение практичности multipart для REST — https://stackoverflow.com/questions/30364642/does-using-multipart-form-data-content-type-for-a-restful-post-api-a-good-practi.)
- Проверяйте и валидацию Content‑Type на сервере. Не полагайтесь на клиентские заголовки без проверки.
- Ограничьте размер загружаемых файлов и используйте стриминг на сервере, чтобы не держать весь файл в памяти.
- Для передачи файлов между API и сервисами используйте multipart; не присылайте файлы в urlencoded и не храните двоичные данные в строковых полях.
- Тестируйте кодировку имён файлов и текстовых полей — разные клиенты/серверы по‑разному работают с заголовками и charset.
- Если в интеграции участвуют 1С или другие специфичные платформы, проверьте их требования к Content‑Type (частые кейсы: multipart/form-data 1с).
- Для отладки удобно смотреть «сырое» тело запроса; curl -v и специальные утилиты покажут границы и заголовки.
Короткий чек‑лист при выборе:
- Есть файлы/бинар → multipart/form-data.
- Только параметры и маленький текст → application/x-www-form-urlencoded (или лучше — application/json).
- Нужны имена файлов и mime‑типы → multipart.
- Большой текст (не‑ASCII) → подумайте про JSON или multipart с charset.
Источники
- https://developer.mozilla.org/ru/docs/Web/HTTP/Methods/POST
- https://ru.wikipedia.org/wiki/Multipart/form-data
- https://habr.com/ru/articles/511114/
- https://gist.github.com/joyrexus/524c7e811e4abf9afe56
- https://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data
- https://stackoverflow.com/questions/26723467/postman-chrome-what-is-the-difference-between-form-data-x-www-form-urlencoded
- https://stackoverflow.com/questions/30364642/does-using-multipart-form-data-content-type-for-a-restful-post-api-a-good-practi
- https://medium.com/@codingscenes/application-x-www-form-urlencoded-and-multipart-form-data-are-two-different-formats-for-3678a10073e9
Заключение
Выбор между application/x-www-form-urlencoded и multipart/form-data для API сводится к одному вопросу: нужно ли отправлять файлы/бинар и per‑part метаданные? Если да — multipart/form-data; если нет — чаще удобнее application/x-www-form-urlencoded (для простых полей) или ещё лучше application/json для структурированных payload. Учтите рост размера из‑за percent‑кодирования у urlencoded и накладные MIME‑заголовки у multipart; тестируйте на реальных объёмах и используйте проверенные парсеры на сервере.