curl POST-запрос: как отправить JSON, заголовки и токен
Коротко: чтобы отправить JSON через curl, передайте его флагом -d и добавьте заголовок Content-Type: application/json. Писать -X POST не обязательно: раз есть тело, curl сам выберет POST.
curl https://playground.tetiva.app/post \
-H "Content-Type: application/json" \
-d '{"name": "Тетива", "qty": 2}'
Без заголовка curl отправит тело как данные формы, и многие API ответят 400 или 415. В curl 7.82 и новее оба шага заменяет один флаг --json.
Команду можно запустить прямо сейчас. Адрес ведёт на наш тестовый API: он возвращает запрос в том виде, в каком его получил сервер.
Из чего состоит команда#
Программа curl отправляет HTTP-запросы из терминала. Она есть в macOS и в Windows 10 и 11, а в Linux обычно уже установлена или ставится из пакетов.
Адрес. Его можно писать первым или последним, curl разберётся. Кавычки нужны, только если в адресе есть & или ?: иначе оболочка поймёт & по-своему и запустит команду в фоне.
Заголовок, -H. Пишется как "Имя: значение". Заголовков бывает сколько угодно, на каждый свой -H.
Тело, -d. Всё, что после него, уйдёт на сервер как есть. JSON берут в одинарные кавычки, чтобы двойные внутри дошли до curl нетронутыми. Это работает в macOS, Linux и Git Bash. В Windows правила другие, о них ниже.
Нужен ли -X POST#
С -d указывать -X POST не нужно. Если запустить curl -v -X POST -d ..., curl сам об этом напишет: Note: Unnecessary use of -X or --request, POST is already inferred.
-X нужен в двух случаях:
- POST без тела, например чтобы перезапустить задачу. Без
-Xcurl отправил бы GET. - Другие методы с телом:
-X PUT -d '...'или-X PATCH -d '...'.
curl -X POST https://api.example.com/jobs/42/restart
curl -X DELETE https://api.example.com/orders/42
Для DELETE, как во второй строке, тело обычно не нужно.
GET-запрос в curl#
Команда curl с одним адресом отправляет GET и печатает тело ответа. Параметры пишут прямо в адресе, а сам адрес берут в кавычки из-за ? и &:
curl "https://playground.tetiva.app/get?page=2&limit=10"
Тестовый API вернёт параметры в поле args. Чтобы увидеть ещё код ответа и заголовки, добавьте -i.
Что видит сервер: JSON или форма#
Бывает, что тело до сервера дошло, но он не понял, что это JSON. В ответе тестового API это видно сразу: с заголовком данные попадают в поле json, без него в form.
Если тело не разобралось, проверьте две вещи: есть ли заголовок и не сломались ли кавычки. Невалидный JSON вроде {name:Тетива} сервер тоже не прочитает.
У свежего curl есть сокращение --json. Оно кладёт данные в тело и само ставит Content-Type: application/json и Accept: application/json:
curl --json '{"name": "Тетива"}' https://playground.tetiva.app/post
Флаг появился в curl 7.82.0 весной 2022 года. Проверить версию можно командой curl --version. В старых сборках Windows 10 встроенный curl может оказаться старше.
POST с формой и файлом#
Если API ждёт обычную форму, а не JSON, заголовок не нужен: -d сам ставит Content-Type: application/x-www-form-urlencoded. Поля разделяют &:
curl https://playground.tetiva.app/post \
-d 'name=Tetiva&qty=2'
Файл отправляют флагом -F. С ним curl собирает тело multipart/form-data, как браузер при загрузке файла, и тоже сам выбирает POST. @ перед именем значит «взять содержимое файла»:
curl https://playground.tetiva.app/post \
-F 'file=@photo.jpg' \
-F 'name=Tetiva'
Тестовый API вернёт поля в form, а файл в files.
Тело из файла#
Когда JSON длинный, в нём кириллица или много кавычек, проще положить его в файл и передать через @:
curl https://playground.tetiva.app/post \
-H "Content-Type: application/json" \
--data-binary @order.json
Почему --data-binary, а не -d @order.json: -d выбрасывает из файла переводы строк. JSON от этого не ломается, а вот текст, где переносы важны, сломается. --data-binary отправляет файл байт в байт. Вместо имени файла можно написать @-, и curl прочитает тело из стандартного ввода.
Если в сам JSON нужно подставить переменную оболочки, тело берут в двойные кавычки, а внутренние экранируют: -d "{\"name\": \"$NAME\"}". Если в значении могут оказаться кавычки, надёжнее собрать тело через jq и передать его curl через стандартный ввод:
jq -n --arg name "$NAME" '{name: $name}' \
| curl --json @- https://playground.tetiva.app/post
Заголовки и токен#
Токен передают заголовком Authorization. Тестовый API проверяет его на /bearer:
curl https://playground.tetiva.app/bearer \
-H "Authorization: Bearer abc123"
Без заголовка тот же адрес ответит 401 и напишет missing Authorization header. Если токен передан, а сервер его всё равно не принимает, загляните в статью про Bearer-токены.
Токен удобно держать в переменной окружения: TOKEN=abc123, а в команде писать -H "Authorization: Bearer $TOKEN". Кавычки здесь двойные: в одинарных оболочка не подставит переменную и отправит буквально $TOKEN.
Команда с токеном остаётся в истории терминала и в любом чате, куда её вставили. Как ключи утекают таким путём, рассказано в статье про токен на скриншоте.
Для логина и пароля есть -u: curl -u user:passwd https://playground.tetiva.app/basic-auth/user/passwd. curl сам соберёт заголовок Basic.
Чтобы увидеть, что ушло на самом деле, добавьте -v. Строки с > покажут отправленные заголовки, строки с < полученные. Флаг -i добавит заголовки ответа к выводу без остальных подробностей.
curl в Windows: cmd и PowerShell#
curl встроен в Windows 10 начиная с версии 1803 и в Windows 11, но команды для bash там не работают как есть. В cmd JSON берут в двойные кавычки, а внутренние экранируют \", строку переносят символом ^. В Windows PowerShell 5.1 вместо curl пишут curl.exe.
Командная строка (cmd). Одинарные кавычки cmd не понимает. JSON берут в двойные, а кавычки внутри экранируют обратной косой чертой. Строку переносят символом ^:
curl https://playground.tetiva.app/post ^
-H "Content-Type: application/json" ^
-d "{\"name\": \"Tetiva\", \"qty\": 2}"
PowerShell. В Windows PowerShell 5.1, который стоит в системе, curl означает вовсе не curl, а встроенную команду Invoke-WebRequest. Поэтому флаги curl вызывают ошибку про параметр Headers. Настоящий curl вызывается как curl.exe. Строки в PowerShell переносят обратным апострофом.
Кавычки внутри JSON PowerShell тоже может потерять по дороге к curl.exe, и тогда сервер получит сломанное тело. Если не хочется в этом разбираться, используйте файл: curl.exe ... --data-binary "@order.json". Он одинаково работает в cmd, PowerShell и bash. В PowerShell @order.json берут в кавычки, потому что @ там тоже имеет особое значение.
В PowerShell 7 псевдонима curl нет, и curl сразу запускает curl.exe, а с версии 7.3 JSON в одинарных кавычках доходит до него без потерь.
Частые ошибки#
| Что видите | Почему | Что сделать |
|---|---|---|
415 Unsupported Media Type | Нет Content-Type: application/json | Добавьте -H или замените -d на --json |
400 Bad Request, «invalid JSON» | Оболочка съела кавычки в теле | Положите JSON в файл и отправьте через --data-binary @файл |
| Сервер получил форму вместо JSON | -d без заголовка | Добавьте Content-Type |
401 Unauthorized | Нет заголовка Authorization, нет слова Bearer или токен истёк | Запустите с -v и посмотрите, что ушло |
| В cmd вторая строка команды выполняется отдельно | Перенос \ работает только в bash и zsh | В cmd переносите через ^ или пишите в одну строку |
PowerShell ругается на параметр Headers | curl здесь значит Invoke-WebRequest | Пишите curl.exe |
Если curl не дошёл до ответа вовсе и вернул код 6, 7, 28, 35 или 56, дело не в теле запроса, а в сети: как найти слой, на котором умер запрос.
Как перенести curl в Postman или другой клиент и обратно#
Готовую команду проще всего взять из браузера. Откройте DevTools (F12), вкладку Network, нажмите правой кнопкой на нужный запрос и выберите Copy → Copy as cURL. В Chrome для Windows есть два варианта, (cmd) и (bash): берите тот, что под вашу оболочку. В команде будут все заголовки и cookie, то есть ваша сессия. Если нужен не один запрос, а вся страница целиком, сохраните HAR-файл.
Если длинную команду неудобно править в терминале, её можно вставить в API-клиент. В Postman для этого есть Import, куда вставляют текст команды. В Tetiva команду вставляют прямо в адресную строку HTTP-запроса: метод, адрес, заголовки, тело и авторизация разложатся по вкладкам, а уведомление перечислит, что приехало. Если вставка затёрла заполненные поля, рядом будет кнопка Undo.
Чтобы получить из запроса команду curl, выберите Copy as cURL в меню рядом с кнопкой Send. Переменные вида {{token}} в команде уже подставлены, вместе со значениями секретов. Прежде чем отправлять такую команду в чат, замените токен на заглушку. Подробности, включая то, как в команду попадают pre-скрипты и cookie, есть в документации по импорту и экспорту.
Попробуйте сами#
- Отправьте команду из начала статьи и найдите в ответе поле
jsonсо своими данными. - Уберите
-H "Content-Type: application/json"и посмотрите, как данные переедут вform. - Отправьте запрос на
/bearerбез заголовка и с ним: должны прийти401и200. - Вставьте любую из команд в свой API-клиент и отправьте её оттуда.
Все адреса для тренировки, включая медленные ответы и любые коды ошибок, собраны на странице тестового API.