Как параметры отправляются в запросе HTTP POST?

1476

В запросе HTTP GET параметры отправляются в виде строки запроса :

http://example.com/page ? параметр = значение & также = другое

В запросе HTTP POST параметры не отправляются вместе с URI.

Где значения? В заголовке запроса? В теле запроса? На что это похоже?

Камило Мартин
источник

Ответы:

1255

Значения отправляются в теле запроса в формате, который указывает тип содержимого.

Обычно это тип контента application/x-www-form-urlencoded, поэтому тело запроса использует тот же формат, что и строка запроса:

parameter=value&also=another

Когда вы используете загрузку файла в форме, multipart/form-dataвместо этого вы используете кодировку, которая имеет другой формат. Это сложнее, но обычно вам не нужно заботиться о том, как это выглядит, поэтому я не буду показывать пример, но было бы полезно знать, что он существует.

Guffa
источник
25
Я забыл о разной загрузке файлов (+ 1 / принято). Ваш ответ достаточен, хотя было бы очень приятно, если бы у него было больше информации multipart/form-data. Для тех, кто заинтересован, вот вопрос об этом .
Камило Мартин
73
ПРИМЕЧАНИЕ : тело отделено от заголовка только одной пустой строкой .
Габ 是 好人
2
Вы объяснили, что мы помещаем в HTTPBody, но что мы помещаем / пишем в HTTPHeader? Какой цели это служит?
Мед
4
@Honey: HTTP-заголовок для сообщения выглядит как заголовок для получения, но с глаголом POST вместо GET и значением типа контента (и необязательным значением длины контента), так как запрос имеет контент (тело). Каждый тип запроса имеет заголовок, некоторые типы также имеют тело.
Гуффа
4
@KennethWorden Нет, ни один из методов не будет правильно отправлять JSON. однако вы можете загрузить файл json в форме, закодированной multipart/form-dataили, если вы отвечаете за создание запроса, изменить тип содержимого на application/jsonи вставить текст json непосредственно в текст http
Cholthi Paul Ttiopic
428

Содержимое помещается после заголовков HTTP. Формат HTTP POST должен иметь заголовки HTTP, сопровождаемые пустой строкой и телом запроса. Переменные POST хранятся в виде пар ключ-значение в теле.

Вы можете увидеть это в необработанном содержании HTTP-сообщения, показанном ниже:

POST /path/script.cgi HTTP/1.0
From: frog@jmarshall.com
User-Agent: HTTPTool/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32

home=Cosby&favorite+flavor=flies

Это можно увидеть с помощью такого инструмента, как Fiddler , который можно использовать для просмотра необработанных HTTP-запросов и полезных нагрузок ответов, передаваемых по проводам.

Джо Альфано
источник
39
Только если тип контента есть application/x-www-form-urlencoded, что не всегда так.
Гуффа
@ Камило Мартин .... [+1] за отличный вопрос & @ Джо Альфано .... [+1] за отличный ответ ....... Теперь у меня есть четкое представление о запросе POST .... но если изображение приходит вместе с ключом, парой значений и информацией данных ..... Как выглядит структура POST?
Деврат
9
@ Джо, а зачем тебе там Fromзаголовок?
Pacerier
@ Джо, я люблю случайное включение Fromзаголовка. ИМО это там с кодом статуса 418 HTTP.
Том Ховард
как добавить аутентификацию пользователя и пароля?
m4l490n
376

Краткий ответ: в запросах POST значения отправляются в «теле» запроса. С веб-формами они, скорее всего, отправляются с типом носителя application/x-www-form-urlencodedили multipart/form-data. Языки программирования или рамки , которые были предназначены для обработки веб-запросов , как правило , делают «The Right Thing ™» с такими запросами и предоставить вам легкий доступ к легко расшифрованных значений (например , $_REQUESTили $_POSTв PHP, или cgi.FieldStorage(), flask.request.formв Python).


Теперь давайте немного отвлечемся, что может помочь понять разницу;)

Разница между GETи POSTзапросами в основном семантической. Они также «используются» по-разному, что объясняет разницу в передаче значений.

GET ( соответствующий раздел RFC )

При выполнении GETзапроса вы запрашиваете у сервера одну или несколько сущностей. Чтобы позволить клиенту фильтровать результат, он может использовать так называемую «строку запроса» URL-адреса. Строка запроса является частью после ?. Это часть синтаксиса URI .

Таким образом, с точки зрения кода вашего приложения (части, которая получает запрос), вам нужно будет проверить часть запроса URI, чтобы получить доступ к этим значениям.

Обратите внимание, что ключи и значения являются частью URI. Браузеры могут наложить ограничение на длину URI. Стандарт HTTP гласит, что ограничений нет. Но на момент написания этой статьи, большинство браузеров действительно ограничивают идентификаторы URI (я не имею конкретных значений). GETЗапросы никогда не должны использоваться для отправки новой информации на сервер. Особенно не большие документы. Вот где вы должны использовать POSTили PUT.

POST ( соответствующий раздел RFC )

При выполнении POSTзапроса клиент фактически отправляет новый документ на удаленный хост. Таким образом, строка запроса (семантически) не имеет смысла. Вот почему у вас нет доступа к ним в коде приложения.

POSTнемного более сложный (и способ более гибкий):

При получении запроса POST вы всегда должны ожидать «полезную нагрузку» или, в терминах HTTP: тело сообщения . Тело сообщения само по себе довольно бесполезно, так как нет стандартного (насколько я могу судить. Может быть, application / octet-stream?) Формата. Формат тела определяется Content-Typeзаголовком. При использовании FORMэлемента HTML с method="POST", это обычно application/x-www-form-urlencoded. Другой очень распространенный тип - multipart / form-data, если вы используете загрузку файлов. Но это может быть что угодно , начиная от text/plain, сверх application/jsonили даже обычай application/octet-stream.

В любом случае, если POSTзапрос сделан с помощью, Content-Typeкоторый не может быть обработан приложением, он должен вернуть 415код состояния .

Большинство языков программирования (и / или веб-фреймворков) предлагают способ де / кодирования тела сообщения из / в наиболее распространенные типы (например application/x-www-form-urlencoded, multipart/form-dataили application/json). Так легко. Пользовательские типы потенциально требуют немного больше работы.

Используя в качестве примера стандартный кодированный HTML-документ, приложение должно выполнить следующие шаги:

  1. Читать Content-Typeполе
  2. Если значение не относится к поддерживаемым типам мультимедиа, вернуть ответ с 415кодом состояния
  3. в противном случае декодируйте значения из тела сообщения.

Опять же, языки вроде PHP или веб-фреймворки для других популярных языков, вероятно, справятся с этим за вас. Исключением является 415ошибка. Никакая структура не может предсказать, какие типы контента выберет ваше приложение для поддержки и / или не поддержки. Это зависит от вас.

PUT ( соответствующий раздел RFC )

PUTЗапрос в значительной степени обрабатываются точно так же, как POSTзапрос. Большая разница состоит в том, что POSTзапрос должен позволить серверу решить, как (и вообще ли) создать новый ресурс. Исторически (из устаревшего RFC2616 он должен был создать новый ресурс как «подчиненный» (дочерний) URI, в который был отправлен запрос).

PUTЗапрос в отличии предполагается «депозит» ресурс именно на этом URI, и именно это содержание. Не больше, не меньше. Идея заключается в том , что клиент несет ответственность за ремесло полного ресурса , прежде чем «Собирает» его. Сервер должен принять это как есть на данном URL.

Как следствие, POSTзапрос обычно не используется для замены существующего ресурса. PUTЗапрос может сделать как создать и заменить.

Примечание

Существуют также « параметры пути », которые можно использовать для отправки дополнительных данных на удаленный компьютер, но они настолько необычны, что я не буду вдаваться в подробности. Но, для справки, вот выдержка из RFC:

Помимо точечных сегментов в иерархических путях, сегмент пути считается непрозрачным по общему синтаксису. Приложения, генерирующие URI, часто используют зарезервированные символы, разрешенные в сегменте, для разграничения подкомпонентов, специфичных для схемы или обработчика разыменования. Например, зарезервированные символы точки с запятой (";") и равенства ("=") часто используются для разделения параметров и значений параметров, применимых к этому сегменту. Запятая (",") зарезервированный символ часто используется для аналогичных целей. Например, один производитель URI может использовать сегмент, такой как «name; v = 1.1», чтобы указать ссылку на версию 1.1 «name», тогда как другой может использовать сегмент, такой как «name, 1.1», чтобы указать то же самое. Типы параметров могут быть определены специфичной для схемы семантикой,

exhuma
источник
1
Возможно, я действительно немного коснулся. Я добавил "tl; dr" в начало ответа, что должно сделать его более понятным.
exhuma
Я также только что отредактировал его, ссылаясь на RFC7231 вместо RFC2616 (который давно устарел). Основное отличие этого ответа, кроме обновленных ссылок, заключается в разделе «PUT».
exhuma
Я думал, что PUT обрабатывается иначе, чем POST, поскольку он должен быть идемпотентным? stackoverflow.com/questions/611906/…
rogerdpack
1
@rogerdpack Вы не ошиблись. Если прочитать второй абзац в PUTразделе, вы увидите , что она является идемпотентом. POSTв отличие от этого - по определению - не может быть. POSTвсегда будет создавать новый ресурс. PUTбудет, если идентичный ресурс существует заменить его. Поэтому, если вы позвоните POST10 раз, вы создадите 10 ресурсов. Если вы позвоните PUT10 раз, он (возможно) создаст только один. Это отвечает на ваш вопрос?
exhuma
60

Вы не можете ввести его непосредственно в адресной строке браузера.

Например, вы можете увидеть, как данные POST отправляются в Интернет с помощью живых HTTP-заголовков . Результат будет примерно таким

http://127.0.0.1/pass.php
POST /pass.php HTTP/1.1

Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://127.0.0.1/pass.php
Cookie: passx=87e8af376bc9d9bfec2c7c0193e6af70; PHPSESSID=l9hk7mfh0ppqecg8gialak6gt5
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
username=zurfyx&pass=password

Где это говорит

Content-Length: 30
    username=zurfyx&pass=password

будут почтовые значения.

zurfyx
источник
2
Разъяснение: Content-Lengthдолжно быть 29здесь? Это фактическая длина строки username=zurfyx&pass=password.
Бегемот
@Hippo был символом новой строки, предназначенным быть там?
Викингстев
@vikingsteve Я понимаю, что ты имеешь в виду. Так что, я думаю, у Контента всегда есть новая строка в конце.
Бегемот
2
Заголовок отделен от тела дополнительным символом новой строки
Mára Toner
24

Тип носителя по умолчанию в запросе POST это application/x-www-form-urlencoded. Это формат для кодирования пар ключ-значение. Ключи могут быть дубликатами. Каждая пара ключ-значение отделяется &символом, а каждая клавиша отделяется от своего значения =символом.

Например:

Name: John Smith
Grade: 19

Кодируется как:

Name=John+Smith&Grade=19

Это помещается в тело запроса после заголовков HTTP.

Nejat
источник
1
Вы объяснили, что мы помещаем в HTTPBody, но что мы помещаем / пишем в HTTPHeader?
Мед
Вы упомянули, что ключ может быть дубликатом, тогда каков результат такого дубликата? Будет ли последний автоматически перезаписывать предыдущие значения? Спасибо.
Цзинхуэй Ниу
@JinghuiNiu, если ключ повторяется, он должен быть проанализирован как массив. Это очень поздно, но может помочь кому-то еще.
Ханаш Яслем
18

Значения форм в HTTP POST отправляются в теле запроса в том же формате, что и строка запроса.

Для получения дополнительной информации см. Спецификацию .

SLaks
источник
5
«Тот же формат» немного неоднозначно. Они начинаются с, ?например?
Камило Мартин
7
@PeterWooster Да, но не приводится пример. В этом отношении это похоже на ответ, который гласит: «Посмотрите, есть ответ на ваш вопрос в блоге приложения (ссылка) ».
Камило Мартин
36
@PeterWooster Это не нужно, но очень хорошо, когда вы что-то забываете, гуглите, переходите к первой ссылке - ТАК, и есть ясный, краткий пример, который говорит вам, что вам нужно, вместо того, чтобы посылать вам что-нибудь потрогать чрезмерно подробные спецификации, которые, даже если они всеобъемлющие, могут быть непригодны для повышения квалификации. Подумайте об этом: большая часть тестов на этом сайте может сводиться к тому, чтобы «прочитать спецификации / manual / API / etc (ссылка) ». Будет ли это полезно? Не больше, чем Google.
Камило Мартин
2
Только если тип контента есть application/x-www-form-urlencoded, что не всегда так.
Гуффа
3
Формат строки запроса GET отличается от формата application / x-www-form-urlencoded. Например, пробелы кодируются по-разному (% 20 против +). Ответ вводит в заблуждение в этом отношении.
UnclickableCharacter
18

Некоторые веб-сервисы требуют, чтобы вы размещали данные запроса и метаданные отдельно. Например, удаленная функция может ожидать, что подписанная строка метаданных включена в URI, а данные размещаются в HTTP-теле.

Запрос POST может семантически выглядеть так:

POST /?AuthId=YOURKEY&Action=WebServiceAction&Signature=rcLXfkPldrYm04 HTTP/1.1
Content-Type: text/tab-separated-values; charset=iso-8859-1
Content-Length: []
Host: webservices.domain.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: identity
User-Agent: Mozilla/3.0 (compatible; Indy Library)

name    id
John    G12N
Sarah   J87M
Bob     N33Y

Этот подход логически объединяет QueryString и Body-Post, используя единый объект, Content-Typeкоторый является «инструкцией синтаксического анализа» для веб-сервера.

Обратите внимание: HTTP / 1.1 заключен в #32(пробел) слева и #10(перевод строки) справа.

Интерфейс неизвестен
источник
Разница между /user/johnи /?user=johnявляется просто семантической (HTTP на самом деле не дает особого отношения к строкам запроса), поэтому я воспринимаю это как разумное ожидание. Но что вы имеете в виду под «завернутым в пространство слева»? До метода HTTP нет пробелов. Вы имеете в виду пустую строку для тела сообщения?
Камило Мартин
Между ...Ym04и HTTP/1.1в приведенном выше коде есть пробел (ASCII # 32) . Таким образом, QueryString просто находится между глаголом и версией протокола.
Интерфейс неизвестен
1
Ваше примечание заставляет это звучать, как будто это что-то неожиданное и специфичное для версии. Откровенно говоря, кажется очевидным, что там есть место. И перевод строки также относится и к другим строкам, как и все Unix.
Камило Мартин
1
Я просто подчеркнул, что я не мог выделить в коде. Это может показаться очевидным, но иногда это не так.
Интерфейс неизвестен
Это правда, что мы могли бы передавать параметры запроса как часть URL, разделяя URI и параметры так же, ?как мы делаем с GETзапросами.
просит
8

Прежде всего, давайте различать GETиPOST

Получить: Это по умолчанию HTTPзапрос , который сделан на сервер и используется для извлечения данных из сервера и строки запроса , который приходит после того, как ?в URIиспользуются для получения уникального ресурса.

это формат

GET /someweb.asp?data=value HTTP/1.0

здесь data=valueпередано значение строки запроса.

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

POST /somweb.aspHTTP/1.0
Host: localhost
Content-Type: application/x-www-form-urlencoded //you can put any format here
Content-Length: 11 //it depends
Name= somename

Почему ПОСТ над GET?

В GETзначение, отправляемое на серверы, обычно добавляются к базовому URL в строке запроса, теперь есть 2 последствия этого

  • Эти GETзапросы сохраняются в истории браузера с параметрами. Таким образом, ваши пароли остаются незашифрованными в истории браузера. Это было настоящей проблемой для Facebook в те времена.
  • Обычно серверы имеют ограничение на продолжительность URI. Если отправлено слишком много параметров, вы можете получить414 Error - URI too long

В случае отправки запроса ваши данные из полей будут добавлены в тело. Длина параметров запроса вычисляется и добавляется в заголовок для длины содержимого, и к URL-адресу не добавляются важные данные.

Вы можете использовать сетевой раздел Инструментов Google для разработчиков, чтобы увидеть основную информацию о том, как запросы поступают на серверы.

и вы всегда можете добавить больше значения в вашем Request HeadersКак и Cache-Control, Origin, Accept.

Зеешан Адиль
источник
4
Предположения о безопасности верны только в контексте HTTPSсвязи, а не HTTP. HTTPSшифрует как URL(включая параметры запроса), так и Request Body, когда HTTPшифрует / защищает ни то, ни другое. Описанная проблема возникает из-за того, что многие браузеры хранят URIs(в том числе URLs) в своих базах данных истории (обычно не зашифрованных). Таким образом, используйте только Request Body+ HTTPSдля чего-то чувствительного.
Петру Захария
@PetruZaharia Я согласен с твоим объяснением. Вы также можете предложить это как редактировать, и я буду рад принять! :)
Зеешан Адил