HTTP GET з тілом запиту


2109

Я розробляю новий RESTful веб-сервіс для нашого додатку.

Роблячи GET для певних організацій, клієнти можуть запитувати вміст об'єкта. Якщо вони хочуть додати деякі параметри (наприклад, сортування списку), вони можуть додати ці параметри в рядок запиту.

Крім того, я хочу, щоб люди могли вказати ці параметри в тілі запиту. Здається, HTTP / 1.1 цього прямо не забороняє. Це дозволить їм вказати більше інформації, а також спростить вказівку складних запитів XML.

Мої запитання:

  • Це взагалі гарна ідея?
  • Чи будуть у клієнтів HTTP проблеми з використанням органів запиту в GET-запиті?

http://tools.ietf.org/html/rfc2616


552
Перевага полягає в тому, що дозволяє легко надсилати XML або JSON органи запитів, воно не має обмеження по довжині і простіше кодувати (UTF-8).
Еверт

29
Якщо ви шукаєте, це безпечний і безвідмовний метод, який дозволяє органам запитів, ви можете переглянути ПОШУК, ПРОПФІНД і ЗВІТ. Звичайно, не використовуючи GET і тіло запиту перемагає кешування більш-менш.
Джуліян Решке

226
@fijiaaron: Це через 3 роки, і з тих пір я набув великий досвід написання веб-служб. Це в основному все, що я робив за останні кілька років. Я сміливо можу сказати, що це дійсно дуже погана ідея додати тіло до GET-запиту. Два найкращих відповіді стоять як скеля.
Еверт

26
@Ellesedil: Простіше кажучи: будь-які переваги щодо використання GET над POST існують через те, як створено HTTP. Ці переваги більше не існують, коли ви порушуєте стандарт таким чином. Тому залишається лише одна причина використовувати GET + тіло запиту замість POST: Естетика. Не жертвуйте надійним дизайном над естетикою.
Еверт

7
Щоб підкреслити те, що сказав Еверт: "у нього немає обмеження по довжині". Якщо ваш GET з параметрами запиту порушує обмеження довжини (2048), то який інший вибір є, крім того, щоб розмістити інформацію про рядок запиту в об'єкт json, наприклад, в тілі запиту.
Кіеран Раян

Відповіді:


1724

Коментар Роя Філдінга щодо включення органу із запитом GET .

Так. Іншими словами, будь-яке повідомлення HTTP-запиту може містити тіло повідомлення, і, таким чином, слід розбирати повідомлення, враховуючи це. Однак семантика сервера для GET обмежена таким чином, що тіло, якщо воно є, не має семантичного значення для запиту. Вимоги до розбору окремо від вимог до семантики методу.

Так, так, ви можете надіслати тіло за допомогою GET, і ні, це ніколи не корисно.

Це частина шаруватого дизайну HTTP / 1.1, який стане зрозумілим знову після розподілу специфікації (робота в процесі роботи).

.... Рой

Так, ви можете надіслати орган запиту за допомогою GET, але він не повинен мати жодного значення. Якщо ви надаєте йому значення, аналізуючи його на сервері та змінюючи свою відповідь на основі її вмісту , ви ігноруєте цю рекомендацію в специфікації HTTP / 1.1, розділ 4.3 :

[...] якщо метод запиту не включає визначену семантику для об'єкта-суті, то тіло повідомлення БУДЕ ігноруватися при обробці запиту.

Опис методу GET у специфікації HTTP / 1.1, розділ 9.3 :

Метод GET означає отримати будь-яку інформацію ([...]), ідентифіковану URI-запитом.

де зазначено, що орган запиту не є частиною ідентифікації ресурсу в GET-запиті, а лише URI запиту.

Оновлення RFC2616, на яке посилається "HTTP / 1.1 специфікація", тепер застаріло. У 2014 році його замінили RFC 7230-7237. Цитата "тіло повідомлення БУДЕ ігноровано під час обробки запиту" було видалено. Тепер просто "Запросити обрамлення повідомлення не залежить від семантики методу, навіть якщо метод не визначає жодного використання для тіла повідомлення". 2-а цитата "Метод GET означає отримати будь-яку інформацію ... ідентифікується Request-URI" було видалено. - З коментаря


71
Кешування / проксі - це дві речі, які ви, швидше за все, зламаєте, так. "Семантика" - це лише інший спосіб сказати, "як люди, які виготовляють інші компоненти, очікуватимуть функціонування інших компонентів". Якщо ви порушите семантику, ви, швидше за все, побачите, що все порушиться там, де люди написали речі, які очікували, що ви будете шанувати цю семантику.
Стюарт П. Бентлі

108
Elasticsearch - це досить великий продукт, який використовує HTTP-органи запитів у GET. Відповідно до їх посібника, чи HTTP-запит повинен підтримувати тіло чи ні, не визначено. Мені особисто не подобається заповнення органу запиту GET, але, схоже, вони мають іншу думку, і вони повинні знати, що вони роблять. elastic.co/guide/en/elasticsearch/guide/current/…
GordonM

25
@iwein надання органам запитів GET значення насправді не є порушенням специфікації. HTTP / 1.1 вказує, що сервери ДОЛЖНЯ ігнорувати тіло, але RFC 2119 вказує, що реалізаторам дозволено ігнорувати пункти "ДОБРО", якщо у них є вагомі причини для цього. Швидше за все , клієнт робить порушує специфікацію , якщо він передбачає , що зміна тіла GET буде НЕ змінить відповідь.
Еміль Лундберг

107
RFC2616, на який посилається "HTTP / 1.1 специфікація", тепер застарілий. У 2014 році його замінили RFC 7230-7237. Цитата " тіло повідомлення БУДЕ ігноровано під час обробки запиту " було видалено . Тепер просто " Запросити обрамлення повідомлення не залежить від семантики методу, навіть якщо метод не визначає жодного використання для тіла повідомлення ". 2-а цитата " Метод GET означає отримати будь-яку інформацію ... ідентифікується Request-URI " було видалено . Тож пропоную відредагувати відповідь @Jarl
Артем Наконечний

28
Я знаю, що це стара нитка - я натрапив на неї. @Artem Nakonechny технічно вірно, але нова специфікація говорить: " Навантаження в повідомленні GET-запиту не має визначеної семантики; пересилання органу корисного навантаження на GET-запит може спричинити відхилення запиту в деяких існуючих реалізаціях". Тож це все-таки не дуже гарна ідея, якщо її можна уникнути.
fastcatch

289

Хоча ви можете це зробити, наскільки це явно не виключається специфікацією HTTP, я б запропонував уникнути цього просто тому, що люди не очікують, що речі працюватимуть таким чином. У ланцюжку запитів HTTP існує багато фаз, і хоча вони "в основному" відповідають специфікації HTTP, єдине, в чому ви впевнені, - це те, що вони будуть вести себе як традиційно використовувані веб-браузерами. (Я думаю про такі речі, як прозорі проксі, акселератори, набір інструментів A / V тощо)

Це дух, що стоїть за Принципом надійності, приблизно "бути ліберальним у тому, що ти приймаєш, і консервативним у тому, що ти надсилаєш", ти не хочеш просувати межі специфікації без поважних причин.

Однак, якщо у вас є вагомі причини, ідіть до цього.


228
Принцип стійкості є помилковим. Якщо ви ліберальні в тому, що приймаєте, ви отримаєте лайно, якщо матимете успіх щодо прийняття, просто тому, що приймаєте лайно. Це ускладнить вам розвиток вашого інтерфейсу. Подивіться лише на HTML. Ось такий принцип відштовхування в дії.
Євген Бересовський

27
Я думаю, що успіх і широта прийняття (і зловживання) протоколів говорить про значення принципу надійності.
caskey

39
Ви коли-небудь намагалися розібрати справжній HTML? Сам реалізувати це неможливо, тому майже всі - включаючи дійсно великих гравців, таких як Google (Chrome) та Apple (Safari), цього не зробили, але покладалися на існуючі реалізації (врешті-решт, вони покладалися на KHTML KDE). Це повторне використання, звичайно, приємне, але ви намагалися відображати HTML у додатку .net? Це кошмар, оскільки вам або доведеться вбудовувати некерований компонент IE (або подібний) з його проблемами та збоями, або ви використовуєте доступний (на кодеплекс) компонент, керований, який навіть не дозволяє вам вибрати текст.
Євген Бересовський

6
Не тільки специфікація HTTP дозволяє отримувати дані тіла із запитом GET, але це також звичайна практика: Популярний _search API двигуна ElasticSearch рекомендує GET-запити із запитом, доданим у тілі JSON. Будучи поступкою для незавершених реалізацій HTTP-клієнтів, він також дозволяє тут просити POST-запити.
Крістіан Піетш

3
@ChristianPietsch, сьогодні це звичайна практика. Чотири роки тому цього не було. Хоча специфікація явно дозволяє клієнту необов'язково включати (МОЖЕ) суб'єкт у запит (розділ 7), значення МОЖ може бути визначено в RFC2119, і (шалений) проксі-сервер може бути специфічно сумісним, знімаючи об'єкти в GET-запитах, зокрема, поки він не завершується збоєм, він може забезпечити "знижену функціональність", пересилаючи заголовки запиту, а не включену сутність. Крім того, існує безліч правил щодо того, які зміни версії ОБОВ'ЯЗКОВО / МОЖЕ / ДОБРІТЬ бути зроблені при наближенні до різних рівнів протоколу.
caskey

151

Ви, мабуть, зіткнетеся з проблемами, якщо коли-небудь спробуєте скористатися кеш-пам’яттю. Проксі-агенти не збираються шукати в GET тілі, щоб перевірити, чи параметри впливають на реакцію.


10
Використання полів заголовків ETag / Last-Modified допомагає таким чином: коли використовується "умовний GET", проксі-сервери / кеші можуть діяти на цю інформацію.
jldupont

2
@jldupont Кеші використовують наявність валідаторів, щоб знати, чи може відповідь несвіжої відповіді повторно підтверджена, однак вони не використовуються як частина основного або вторинного кеш-ключа.
Даррел Міллер

Ви могли це виправити за допомогою контрольної суми тіла в параметрі запиту
Адріан,

73

Ні ресклієнт, ні консоль REST не підтримують це, але curl це робить.

Специфікація HTTP говорить у розділі 4.3

Орган повідомлення НЕ МОЖЕ бути включений у запит, якщо специфікація методу запиту (розділ 5.1.1) не дозволяє надсилати суб'єкт-орган у запитах.

Розділ 5.1.1 переспрямовує нас до розділу 9.x для різних методів. Жоден з них прямо не забороняє включати тіло повідомлення. Однак ...

Розділ 5.2 говорить

Точний ресурс, ідентифікований запитом в Інтернеті, визначається, вивчаючи і запит-URI, і поле заголовка хосту.

а в розділі 9.3 сказано

Метод GET означає отримати будь-яку інформацію (у формі сутності), ідентифіковану URI-запитом.

Що в сукупності дозволяє припустити, що при обробці GET-запиту серверу не потрібно перевіряти нічого іншого, крім поля Request-URI та Host заголовка.

Підсумовуючи, специфікація HTTP не заважає вам надсилати тіло повідомлення за допомогою GET, але існує достатня неоднозначність, щоб воно не здивувало мене, якби його підтримували не всі сервери.


2
Paw також має можливість підтримувати GET-запити з органами, але це має бути включено в налаштуваннях.
с.Даніель

"Метод GET означає отримання будь-якої інформації (у формі сутності), ідентифікованої URI-запитом." Тоді, чи є технічно незаконним / неправильним наявність кінцевої точки GET, яка отримує всі суб'єкти? Наприклад, GET /contacts/100/addressesповертає колекцію адрес для людини id=100.
Джош М.

Застережена бібліотека Java для тестування API REST не підтримує GET-запит з тілом. Apache HttpClient теж не підтримує його.
Пауло Мерсон

Джанго також підтримує розбір тіла GET
Бруно Фінгер

60

Elasticsearch приймає GET запити з тілом. Навіть здається, що це найкращий спосіб: Посібник з еластичних досліджень

Деякі бібліотеки клієнтів (наприклад, драйвер Ruby) можуть записати команду cry на stdout в режимі розробки, і він широко використовує цей синтаксис.


5
Цікаво було, чому Elasticsearch дозволяє це. Це означає, що цей запит для підрахунку всіх документів із корисним навантаженням на GET-запит curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } }' еквівалентний включенню корисного навантаження в sourceпарам: curl -XGET 'http://localhost:9200/_count?pretty&source=%7B%22query%22%3A%7B%22match_all%22%3A%7B%7D%7D%7D'
arun

40
Складні запити можуть вражати максимальну довжину заголовка http.
с.Даніель

11
Саме читання документації з еластичного пошуку спричинило мене до цього питання, оскільки я вважав поганою практикою включити тіло
PatrickWalker

1
Це навіть не потрібно бути складним запитом. Навіть простий прокрутка може повернути дуже довгий scroll_id (в кластері з багатьма фрагментами), що призведе до перевищення максимальної довжини URL-адреси, якщо додати його.
Brent Hronik

11
Elasticsearch підтримує той же запит, використовуючи POST. Вони вирішили дозволити тіло в GET, оскільки вони вважають, що GET є більш семантично правильним, ніж POST, коли мова йде про запит даних. Смішно, що Elasticsearch так багато згадується в цій темі. Я б не використав жодного прикладу (хоч і з популярного продукту) як привід дотримуватися практики.
DSO

32

Те, що ви намагаєтеся досягти, вже давно робиться набагато більш поширеним методом, який не покладається на використання корисного навантаження з GET.

Ви можете просто побудувати свій певний медіатип пошуку або, якщо ви хочете бути більш RESTful, використовувати щось на зразок OpenSearch та розмістити запит на URI, який сервер проінструктував, скажіть / пошук. Потім сервер може генерувати результат пошуку або створити остаточний URI і перенаправити за допомогою 303.

Це має перевагу дотримуватися традиційного методу PRG, допомагає кешувати посередникам кешувати результати тощо.

При цьому, URI-коди все одно кодуються для будь-якого, що не є ASCII, і так само є application / x-www-form-urlencoded та multipart / form-data. Я рекомендую використовувати це, а не створювати ще один нестандартний формат json, якщо ваш намір полягає в підтримці сценаріїв ReSTful.


4
Ви можете просто побудувати свій конкретний пошуковий медіатип. Чи могли б ви детальніше розробити?
Пьотр Доброгост

2
Тим самим я говорив, що ви можете створити тип медіа, який називається application / vnd.myCompany.search + json, який міститиме тип шаблону пошуку, який ви бажаєте видати клієнту, і клієнт може потім надіслати його як POST. Як я вже підкреслив, для цього вже існує тип медіа, і він називається OpenSearch; повторне використання існуючого типу мультимедіа повинно вибиратися за спеціальним маршрутом, коли ви можете реалізувати свій сценарій із існуючими стандартами.
SerialSeb

14
Це розумно, але надто складно і неефективно. Тепер вам потрібно надіслати POST з вашими критеріями пошуку, отримати URI як відповідь назад від вашого POST, а потім надіслати GET з URI критеріями пошуку на сервер для отримання GET критеріїв і повернути результат вам назад. (За винятком того, що включити URI в URI технічно неможливо, оскільки ви не можете надіслати щось, що може містити до 255 символів, у межах чогось, що може бути не більше 255 символів - тому вам доведеться використовувати частковий ідентифікатор, а потім ваш сервер потрібно знати, як вирішити URI для ваших пошукових критеріїв.)
fijiaaron

29

Ви можете або надіслати GET з тілом, або відправити пошту та відмовитись від релігійної релігійності (це не так вже й погано, 5 років тому був лише один член цієї віри - його коментарі, пов'язані вище).

Це не є чудовими рішеннями, але надсилання тіла GET може запобігти проблемам для деяких клієнтів - і деяких серверів.

Виконання POST може мати перешкоди з деякими рамками RESTish.

Джуліан Решке запропонував вище використовувати нестандартний HTTP-заголовок на кшталт "ПОШУК", який міг би бути елегантним рішенням, за винятком того, що він ще менше шансів на підтримку.

Перерахувати клієнтів, які можуть і не можуть виконувати кожне з перерахованого вище, може бути найпродуктивнішим.

Клієнти, які не можуть надіслати GET разом із тілом (про який я знаю):

  • XmlHTTPRequest Fiddler

Клієнти, які можуть надіслати GET разом із тілом:

  • більшість браузерів

Сервери та бібліотеки, які можуть отримати тіло з GET:

  • Апач
  • PHP

Сервери (та проксі), які знімають тіло з GET:

  • ?

2
Кальмар 3.1.6 також знімає тіло GET, коли Довжина вмісту дорівнює 0 або не встановлено, і в іншому випадку надсилає назад необхідну довжину HTTP 411, навіть якщо встановлена ​​довжина
rkok

2
Фіддлер буде, але це попереджає вас.
Тоддмо

Ви хочете сказати, що SEARCHметод, можливо, буде порушений на цьому шляху? Якщо проксі не розуміють метод, вони, як очікується, перейдуть його так, як є, тому я не дуже впевнений, чому ви думаєте, що це щось порушить ...
Alexis Wilke

22

Я ставив це питання IGF HTTP WG. Зауваження Роя Філдінга (автора документа http / 1.1 у 1998 році) було таким

"... реалізація буде порушена, щоб зробити що-небудь, крім розбору та відхилення цього органу, якщо воно отримане"

RFC 7213 (HTTPbis) заявляє:

"Корисне навантаження в повідомленні запиту GET не має визначеної семантики;"

Зараз здається зрозумілим, що наміром було те, що семантичне значення в органах запитів GET заборонено, а це означає, що орган запиту не може використовуватися для впливу на результат.

Є проксі-сервери, які точно будуть порушать ваш запит різними способами, якщо ви включите тіло в GET.

Отже, підсумовуючи, не робіть цього.


21

Який сервер проігнорує його? - fijiaaron 30 серпня 1212 о 21:27

Наприклад, Google робить гірше, ніж ігнорувати його, він вважатиме це помилкою !

Спробуйте самостійно за допомогою простої сітки:

$ netcat www.google.com 80
GET / HTTP/1.1
Host: www.google.com
Content-length: 6

1234

(Вміст 1234 супроводжується CR-LF, тобто загалом 6 байт)

і ви отримаєте:

HTTP/1.1 400 Bad Request
Server: GFE/2.0
(....)
Error 400 (Bad Request)
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.

Ви також отримуєте 400 поганих запитів від Bing, Apple тощо ..., які обслуговуються AkamaiGhost.

Тому я б не радив використовувати GET-запити з органом.


65
Цей приклад є безглуздим, оскільки зазвичай, коли люди збираються додавати тіло до GETзапитів, це тому, що їх власний користувальницький сервер здатний впоратися з цим. Питання полягає в тому, чи будуть інші "рухомі частини" (браузери, кеші тощо) справно працювати.
Pacerier

6
Це погані запити, оскільки ваша корисна навантаження для GET цієї конкретної кінцевої точки не очікується (або розумною) - це не має нічого спільного з використанням GETу загальному випадку. Випадкове корисне навантаження може порушити POSTтак само легко і повернути те саме 400 Bad Request, якби вміст не був у форматі, який мав сенс у контексті конкретного запиту.
nobar

І не лише на цій кінцевій точці в цілому, а на тій конкретній URL-адресі .
Лоуренс Дол

1
Це не має значення, оскільки це лише серверне впровадження Google за цією URL-адресою. Тому немає сенсу в питанні
Джоель Дакворт

для мене це було корисно, тому що я намагався використовувати функції firebase з запитом get + body, і ця помилка може бути дуже виразною і важко зрозуміти.
scrimau

19

З RFC 2616, розділ 4.3 , "Тіло повідомлення":

Сервер ДОЛЖЕН прочитати та переслати тіло повідомлення на будь-який запит; якщо метод запиту не включає визначену семантику для сутності-органу, то тіло повідомлення БУДЕ ігноруватися при обробці запиту.

Тобто, сервери повинні завжди читати будь-який наданий орган запиту з мережі (перевіряйте Content-Length або читайте фрагменти тіла тощо). Також довірені особи повинні переслати будь-який такий орган запиту, який вони отримують. Потім, якщо RFC визначає семантику для тіла для даного методу, сервер може фактично використовувати тіло запиту для генерації відповіді. Однак якщо RFC цього не робить визначає семантику для тіла, то сервер повинен ігнорувати його.

Це відповідає цитаті Філдінга вище.

Розділ 9.3 "GET" описує семантику методу GET та не згадує органи запиту. Тому сервер повинен ігнорувати будь-який орган запиту, який він отримує на запит GET.


У розділі 9.5 "POST" також не згадуються органи запитів, тому ця логіка є хибною.
CarLuva

9
Розділ @CarLuva POST - каже : «Метод POST використовується для запиту , що сервер походження приймає об'єкт , включений ...» сутність тіло розділ говорить , що «сутність-тіло виходить з тіла повідомлення ...» Таким чином, Розділ POST згадує тіло повідомлення, хоча опосередковано посилаючись на тіло сутності, яке переноситься тілом повідомлення запиту POST.
frederickf

9

Згідно з XMLHttpRequest, це недійсно. Зі стандартного :

4.5.6 send()Метод

client . send([body = null])

Ініціює запит. Необов’язковий аргумент надає тіло запиту. Аргумент ігнорується, якщо метод запиту є GETабо HEAD.

Викидає InvalidStateErrorвиняток, якщо будь-який стан не відкрито або встановлено send()прапор.

Метод повинен виконати наступні дії:send(body)

  1. Якщо стан не відкрито , киньте InvalidStateErrorвиняток.
  2. Якщо send()прапор встановлений, киньте InvalidStateErrorвиняток.
  3. Якщо методом запиту є GETабо HEAD, встановіть тіло на нуль.
  4. Якщо тіло недійсне, перейдіть до наступного кроку.

Хоча, я не думаю, що це повинно, оскільки GET-запит може потребувати великого вмісту.

Отже, якщо ви покладаєтесь на XMLHttpRequest браузера, швидше за все, це не спрацює.


1
знижується через те, що XMLHttpRequest є реалізацією. Це може не відображати фактичну специфікацію, яку він повинен реалізувати.
floum

10
Версія Downvote вище неправильна, якщо деякі реалізації не підтримують надсилання тіла з GET, то це може бути причиною цього не робити, незалежно від специфікації. Я фактично зіткнувся з цією точною проблемою в міжплатформенному продукті, над яким я працюю - лише платформа, що використовує XMLHttpRequest, не змогла надіслати отримання.
pjcard

8

Якщо ви дійсно хочете відправити кешируваний JSON / XML-тіло у веб-додаток, єдиним розумним місцем для розміщення ваших даних є рядок запиту, закодований з RFC4648: Base 64 Encoding з URL та ім'ям файлу Safe Alphabet . Звичайно, ви можете просто урленкодувати JSON і поставити значення значень парам-адреси URL, але Base64 дає менший результат. Майте на увазі, що існують обмеження щодо розміру URL-адреси, див. Яка максимальна довжина URL-адреси в різних браузерах? .

Ви можете подумати, що =символ прокладки Base64 може бути поганим для значення парам-адреси URL, однак, здається, це не так - див. Цю дискусію: http://mail.python.org/pipermail/python-bugs-list/2007-February/037195.html . Однак ви не повинні ставити кодовані дані без імені парама, тому що кодована рядок із накладкою буде інтерпретуватися як парам-ключ із порожнім значенням. Я б використав щось подібне ?_b64=<encodeddata>.


Я думаю, що це досить погана ідея :) Але якби я робив щось подібне, я б замість цього скористався спеціальним заголовком HTTP (і переконайтесь, що я завжди надсилаю назад Vary: у відповідь).
Еверт

Погано чи ні, але можна виконати :) З даними в заголовку є подібні проблеми з розміром даних, див. Stackoverflow.com/questions/686217/… . Однак спасибі за згадування Varyзаголовка, я не знав, що це реальний потенціал.
Гертас

6

Я б не радив цього, це суперечить стандартній практиці, і не пропонує так багато взамін. Ви хочете зберегти тіло для вмісту, а не варіантів.


5

А як щодо кодованих заголовків невідповідних base64? "SOMETHINGAPP-PARAMS: sdfSD45fdg45 / aS"

Обмеження довжини, хм. Ви не можете змусити вашу обробку пошти розрізняти значення? Якщо ви хочете прості параметри, такі як сортування, я не розумію, чому це буде проблемою. Я думаю, це впевненість, яку ти хвилюєш.


Ви можете надсилати будь-які потрібні параметри за допомогою x-префікса, будь-які обмеження по довжині заголовків цілком будуть довільним обмеженням сервера.
Кріс Марісіч

4

Я засмучений тим, що REST як протокол не підтримує OOP, а Getметод є доказом. Як рішення, ви можете серіалізувати свій DTO до JSON, а потім створити рядок запиту. На стороні сервера ви зможете дезаріалізувати рядок запиту до DTO.

Погляньте на:

Підхід на основі повідомлень може допомогти вам вирішити обмеження методу Get. Ви зможете надіслати будь-який DTO, як з органом запиту

Рамка веб-служб Nelibur надає функціональні можливості, якими ви можете користуватися

var client = new JsonServiceClient(Settings.Default.ServiceAddress);
var request = new GetClientRequest
    {
        Id = new Guid("2217239b0e-b35b-4d32-95c7-5db43e2bd573")
    };
var response = client.Get<GetClientRequest, ClientResponse>(request);

as you can see, the GetClientRequest was encoded to the following query string

http://localhost/clients/GetWithResponse?type=GetClientRequest&data=%7B%22Id%22:%2217239b0e-b35b-4d32-95c7-5db43e2bd573%22%7D

8
Вам слід просто використовувати POST. Якщо в URL-адресі є ім'я методу, ви порушуєте принципову конструкцію спокою. Це RPC, використовуйте POST.
Еверт

3
Я не думаю, що це велика справа, у нас більше проблем під час розробки з RESTful url (тобто замовлення / 1). Як на мене, з методом Get щось не так, це несумісне з OOP. А кого байдуже, як виглядає URL-адреса :) Але за допомогою підходу на основі повідомлень ми можемо створити стабільний віддалений інтерфейс, і це дійсно важливо. PS це не RPC, це повідомлення на основі
GSerjo

3
Я думаю, ви пропускаєте всю точку REST. Коли ви кажете, кого цікавить, як виглядає URL-адреса, ну і REST цікавить багато. І чому REST був би сумісний з OOP?
shmish111

Ні, я не просто бачив трохи далі
GSerjo

4

Наприклад, він працює з Curl, Apache та PHP.

Файл PHP:

<?php
echo $_SERVER['REQUEST_METHOD'] . PHP_EOL;
echo file_get_contents('php://input') . PHP_EOL;

Команда консолі:

$ curl -X GET -H "Content-Type: application/json" -d '{"the": "body"}' 'http://localhost/test/get.php'

Вихід:

GET
{"the": "body"}

Веселий експеримент! PHP читатиметься лише $_POSTтоді, коли тіло буде надіслано із запитом POST та application/x-www-form-urlencoded. Це означає, що тіло ігнорується у GETзапиті. В даному випадку: $_GETі $_POSTв цьому випадку в будь-якому випадку є дуже оманливими. Тож краще скористайтесяphp://input
Мартін Музатко

3

IMHO, ви можете просто надіслати JSONзакодований (тобто. encodeURIComponent) В URL, таким чином, ви не порушите HTTPспецифікації і не потрапите JSONна сервер.


28
так, але головне питання - це обмеження довжини тхо, як ми з цим справляємося?
Себас-

2

У вас є список варіантів, які набагато кращі, ніж використання тіла запиту з GET.

Дозвольте «припустимо, що у вас є категорії та елементи для кожної категорії. Обидва повинні бути ідентифіковані id ("catid" / "itemid" заради цього прикладу). Ви хочете сортувати за іншим параметром "sortby" у визначеному "порядку". Ви хочете передати параметри для "sortby" та "order":

Ти можеш:

  1. Використовуйте рядки запитів, наприклад example.com/category/{catid}/item/{itemid}?sortby=itemname&order=asc
  2. Використовуйте mod_rewrite (або подібний) для шляхів: example.com/category/{catid}/item/{itemid}/{sortby}/{order}
  3. Використовуйте окремі заголовки HTTP, які ви передаєте разом із запитом
  4. Використовуйте інший метод, наприклад POST, для отримання ресурсу.

Усі мають свої мінуси, але набагато краще, ніж використання GET з тілом.


0

Навіть якщо популярний інструмент використовує це, як це часто цитується на цій сторінці, я вважаю, що це все ще дуже погана ідея, будучи занадто екзотичною, незважаючи на те, що специфікація не заборонена.

Багато проміжних інфраструктур можуть просто відхиляти такі запити.

Наприклад, забудьте про використання деяких з доступних CDN в передній частині вашого веб - сайту, як цей один :

Якщо GETзапит глядача включає тіло, CloudFront повертає глядачеві код статусу HTTP 403 (Заборонено).

І так, ваші клієнтські бібліотеки також можуть не підтримувати видачу таких запитів, про що повідомляється в цьому коментарі .


0

Я використовую програму RestTemplate Spring Framework у своїй програмі клієнта, і на стороні сервера я визначив GET-запит з тілом Json. Моє основне призначення те саме, що і ваше: коли запит має численні параметри, розміщення їх у тілі здається акуратнішим, ніж введення їх у тривалий рядок URI. Так?

Але, на жаль, це не працює! Сторона сервера кинула таке виняток:

org.springframework.http.converter.HttpMessageNotReadableException: обов'язковий орган запиту відсутній ...

Але я впевнений, що тіло повідомлень правильно надано моїм клієнтським кодом, і що не так?

Я простежив метод RestTemplate.exchange () і виявив наступне:

// SimpleClientHttpRequestFactory.class
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
    ...
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        ...
        if (!"POST".equals(httpMethod) && !"PUT".equals(httpMethod) && !"PATCH".equals(httpMethod) && !"DELETE".equals(httpMethod)) {
            connection.setDoOutput(false);
        } else {
            connection.setDoOutput(true);
        }
        ...
    }
}

// SimpleBufferingClientHttpRequest.class
final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest {
    ...
    protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
        ...
        if (this.connection.getDoOutput() && this.outputStreaming) {
            this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
        }

        this.connection.connect();
        if (this.connection.getDoOutput()) {
            FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
        } else {
            this.connection.getResponseCode();
        }
        ...
    }
}

Зауважте, що у методі ExecuteInternal () вхідний аргумент 'bufferedOutput' містить тіло повідомлення, надане моїм кодом. Я бачив це через налагоджувач.

Однак, завдяки PripravConnection (), getDoOutput () у ExecuteInternal () завжди повертає помилку, що, у свою чергу, робить забуференний вихід повністю ігнорований! Він не скопійований у вихідний потік.

Отже, моя серверна програма не отримала жодного тіла повідомлення і кинула це виняток.

Це приклад про RestTemplate рамки Весна. Справа в тому, що навіть якщо тіло повідомлення більше не заборонено специфікацією HTTP, деякі бібліотеки клієнта або сервера або рамки все ще можуть відповідати старій специфікації і відхиляти тіло повідомлення з GET-запиту.


Ви не читаєте тут специфікацію чи коментарі. Клієнти та сервери, які скидають тіло запиту, знаходяться в межах специфікації. Не використовуйте органи запиту GET
Еверт

@Evert Я не прочитав коментарі правильно чи ти цього не зробив? :) Якщо ви прокрутите до відповіді Пола Моргана (відповідь вгорі) і уважно прочитаєте коментарі, ви побачите це: "RFC2616, на який посилається" специфікація HTTP / 1.1 ", тепер застарілий. У 2014 році його замінили RFCs 7230-7237. Цитата "тіло повідомлення ДОЛЖНЕ проігнорувати при обробці запиту" було видалено. Зараз просто "Запросити кадр повідомлення не залежить від семантики методу, навіть якщо метод не визначає жодного використання для тіла повідомлення ... "
Чжоу

@Evert Крім того, я використовував утиліту REST для тестування "спокійно", щоб перевірити мій сервер Spring-boot. І сервер, і сервер Spring-boot підтримували корпус Json для отримання GET запиту! Лише RestTemplate Sping-Framework скидає тіло від GET-запитів, тож Spring-boot, спокійні та RestTemplate, що / не так?
Чжоу

@Evert Останнє, але не в оренді, я не закликав людей використовувати body у GET-запитах, навпаки, я пропонував НЕ робити це, аналізуючи вихідний код RestTemplate Sping-фреймворку, так чому ви вниз проголосували за мій відповідь?
Чжоу

Я не спростував вашої відповіді. Я просто уточнюю, що будь-яка реалізація HTTP скасовує GET-запит є специфікацією.
Еверт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.