Як параметри надсилаються в HTTP POST-запиті?


1475

У запиті HTTP GET параметри надсилаються як рядок запиту :

http://example.com/page ? параметр = значення & також = інший

У запиті HTTP POST параметри не надсилаються разом з URI.

Де значень? У заголовку запиту? У органі запиту? На що це схоже?

Відповіді:


1252

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

Зазвичай тип вмісту є application/x-www-form-urlencoded, тому тіло запиту використовує той самий формат, що і рядок запиту:

parameter=value&also=another

Під час завантаження файлу у формі ви використовуєте multipart/form-dataкодування, яке має інший формат. Це складніше, але вам зазвичай не потрібно дбати про те, як це виглядає, тому я не показуватиму приклад, але можна добре знати, що він існує.


25
Я забув про різні завантаження файлів (+ 1 / прийнято). Вашої відповіді достатньо, хоча було б дуже приємно, якби у неї було більше інформації multipart/form-data. Для тих, хто цікавиться, ось питання про це .
Каміло Мартін

73
ПРИМІТКА . Тіло відокремлено від заголовка лише одним порожнім рядком .
Габ 是 好人

2
Ви пояснили, що ми розміщуємо в HTTPBody, але що ми розміщуємо / записуємо в HTTPHeader? Якій цілі він служить?
Мед

4
@Honey: Заголовок HTTP для публікації виглядає як один для отримання, але з дієсловом POST замість GET, а також значення типу вмісту (і необов'язкове значення довжини вмісту), оскільки запит має вміст (body). Кожен тип запиту має заголовок, деякі типи також мають тіло.
Guffa

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, що не завжди так.
Guffa

@ Camilo Martin .... [+1] за велике запитання & @ Joe Alfano .... [+1] за чудову відповідь ....... У мене зараз чітке уявлення про POST-запит .... але якщо зображення походить разом із ключовою, ціною парами даних ..... Як виглядає структура POST?
Деврат

9
@Joe, тепер навіщо тобі Fromтам заголовок?
Pacerier

@Joe, я люблю випадкове включення Fromзаголовка. IMO, там з кодом статусу HTTP 418.
Том Говард

як додати автентифікацію користувача та пароля?
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 термінів: тіла повідомлення . Тіло повідомлення само по собі є досить марним, оскільки немає стандартного (наскільки я можу сказати. Може бути, програми / octet-stream?) Формату. Формат тіла визначається Content-Typeзаголовком. При використанні FORMелемента HTML з method="POST", як правило, це application/x-www-form-urlencoded. Ще один дуже поширений тип - це багаточастинні / форма-дані, якщо ви використовуєте завантаження файлів. Але це може бути що завгодно , починаючи від 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" для позначення того ж. Типи параметрів можуть бути визначені семантикою, що залежить від схеми,


1
Можливо, я справді пішов на незначну дотичну. Я додав "tl; dr" у верхній частині відповіді, який повинен зробити його більш зрозумілим.
ексгума

Я також щойно відредагував це на посилання RFC7231 замість RFC2616 (який вже деякий час застарів). Основна відмінність цієї відповіді, окрім оновлених посилань, полягає у розділі "PUT".
ексгума

Я думав, що PUT обробляється інакше, ніж POST, оскільки він повинен бути безсильним? stackoverflow.com/questions/611906 / ...
rogerdpack

1
@rogerdpack Ви не помиляєтеся. Якщо прочитати другий абзац в PUTрозділі, ви побачите , що вона є ідемпотентів. POSTна відміну може - за визначенням - не бути. POSTзавжди створить новий ресурс. PUTбуде, якщо існує ідентичний ресурс, замінить його. Тож якщо ви зателефонуєте POST10 разів, ви створите 10 ресурсів. Якщо ви телефонуєте PUT10 разів, це (можливо) створить лише один. Це відповідає на ваше запитання?
ексгума

59

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

Ви можете бачити, як дані POST надсилаються в Інтернет, наприклад, із заголовками HTTP Live Live . Результат буде чимось подібним

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

будуть значеннями публікації.


2
Пояснення: чи Content-Lengthмає бути 29тут? Це фактична довжина струни username=zurfyx&pass=password.
Бегемот

@Hippo був символом нового рядка, який мав бути там?
vikingsteve

@vikingsteve Я бачу, що ти маєш на увазі. Тож я думаю, що Вміст завжди має новий рядок у кінці його.
Бегемот

2
Заголовок відокремлений від корпусу додатковим
новим

24

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

Наприклад:

Name: John Smith
Grade: 19

Кодується як:

Name=John+Smith&Grade=19

Це розміщується в тілі запиту після заголовків HTTP.


1
Ви пояснили, що ми розміщуємо в HTTPBody, але що ми розміщуємо / записуємо в HTTPHeader?
Мед

Ви згадали, що ключ може бути дублікатом, то який результат такого дублікату? Чи останнє автоматично замінить попереднє значення? Дякую.
Jinghui Niu

@JinghuiNiu, якщо ключ дублюється, його слід розібрати як масив. Це дуже пізно, але може допомогти комусь іншому.
Ханаш Яслем

18

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

Для отримання додаткової інформації див. Специфікацію .


5
"Той самий формат" трохи неоднозначний. Вони починаються з ?прикладу?
Каміло Мартін

7
@PeterWooster Так, але не дає прикладу. У зв'язку з цим, це як відповідь, яка говорить "дивіться, є відповідь на ваше запитання в блозі програми (посилання) ".
Каміло Мартін

36
@PeterWooster Це не потрібно, але це дуже добре, коли ти щось забудеш, гуглиш його, переходиш до першого посилання, яке так, і є чіткий, стислий приклад, який розповідає про те, що потрібно, а не відправляти тебе жувати надмірно деталізовані характеристики, які, навіть якщо є всеосяжними, можуть бути непридатними для освіжувачів. Подумайте над цим: більшість питань якості на цьому веб-сайті можуть звестись до "перегляду читання специфікації / інструкції / API / тощо (посилання) ". Було б корисно? Не більше ніж 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запит за замовчуванням, який робиться на сервер і використовується для отримання даних із сервера та рядка запиту, що надходить ?у a 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

Чому POST над 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 Я згоден з вашим поясненням. Ви також можете запропонувати це як редагувати, і я з радістю прийму! :)
Зеешан Аділь
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.