HTTP GET с телом запроса

2112

Я разрабатываю новый веб-сервис RESTful для нашего приложения.

При выполнении GET для определенных объектов клиенты могут запрашивать содержимое объекта. Если они хотят добавить некоторые параметры (например, сортировать список), они могут добавить эти параметры в строку запроса.

В качестве альтернативы я хочу, чтобы люди могли указывать эти параметры в теле запроса. HTTP / 1.1 явно не запрещает это. Это позволит им указать больше информации, может упростить указание сложных запросов XML.

Мои вопросы:

  • Это хорошая идея в целом?
  • Будут ли у HTTP-клиентов проблемы с использованием тел запросов в GET-запросе?

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

выворачивать наизнанку
источник
553
Преимущество заключается в том, что он позволяет легко отправлять тела запросов XML или JSON, он не имеет ограничений по длине и его проще кодировать (UTF-8).
Эверт
29
Если то, что вы ищете, является безопасным и идемпотентным методом, который позволяет запрашивать тела, вы можете посмотреть на SEARCH, PROPFIND и REPORT. Конечно, отсутствие использования GET и наличие тела запроса более или менее разрушает кеширование.
Джулиан Решке
226
@fijiaaron: Прошло 3 года, и с тех пор я приобрел большой опыт написания веб-сервисов. Это в основном все, что я делал за последние несколько лет. Я могу с уверенностью сказать, что это действительно очень плохая идея - добавить тело к GET-запросу. Лучшие два ответа стоят как скала.
Эверт
26
@Ellesedil: Проще говоря: все преимущества, которые существуют при использовании GET по сравнению с POST, существуют благодаря тому, как устроен HTTP. Эти преимущества больше не существуют, когда вы нарушаете стандарт таким образом. Поэтому остается только одна причина использовать GET + тело запроса вместо POST: эстетика. Не жертвуйте надежным дизайном ради эстетики.
Эверт
7
Чтобы подчеркнуть то, что сказал Эверт: «у него нет ограничения по длине». Если ваш GET с параметрами запроса нарушает ограничение длины (из 2048), то есть другой выбор, кроме как поместить информацию строки запроса в объект json, например, в тело запроса.
Киран Райан

Ответы:

1726

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

Да. Другими словами, любое сообщение HTTP-запроса может содержать тело сообщения и, следовательно, должно анализировать сообщения с учетом этого. Однако семантика сервера для GET ограничена таким образом, что тело, если оно есть, не имеет семантического значения для запроса. Требования к синтаксическому анализу отделены от требований к семантике метода.

Итак, да, вы можете отправить тело с GET, и нет, это никогда не полезно.

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

....Рой

Да, вы можете отправить тело запроса с помощью GET, но оно не должно иметь никакого значения. Если вы придаете ему значение, анализируя его на сервере и изменяя свой ответ на основе его содержимого , то вы игнорируете эту рекомендацию в спецификации HTTP / 1.1, раздел 4.3 :

[...] если метод запроса не включает определенную семантику для тела объекта, то тело сообщения ДОЛЖНО игнорироваться при обработке запроса.

И описание метода GET в спецификации HTTP / 1.1, раздел 9.3 :

Метод GET означает извлечение любой информации ([...]), идентифицированной посредством Request-URI.

в котором говорится, что тело запроса не является частью идентификации ресурса в запросе GET, а только URI запроса.

Обновление RFC2616, упоминаемый как «HTTP / 1.1 spec», теперь устарело. В 2014 году он был заменен RFC 7230-7237. Цитата "тело сообщения ДОЛЖНО игнорироваться при обработке запроса" была удалена. Теперь это просто «Запрос на создание кадра не зависит от семантики метода, даже если метод не определяет никакого использования для тела сообщения». Вторая цитата: «Метод GET означает получение любой информации ... идентифицированной по Request-URI» был удален - из комментария

Пол Морган
источник
72
Кеширование / проксирование - две вещи, которые вы, скорее всего, сломаете, да. «Семантика» - это просто еще один способ сказать, «как люди, которые делают другие компоненты, будут ожидать, что другие компоненты будут работать». Если вы нарушаете семантику, вы с большей вероятностью увидите, что вещи ломаются в тех местах, где люди писали вещи, которые ожидали, что вы соблюдаете эту семантику.
Стюарт П. Бентли
109
Elasticsearch - довольно важный продукт, использующий тела HTTP-запросов в GET. Согласно их руководству, должен ли HTTP-запрос поддерживать наличие тела или нет, не определено. Лично мне неудобно заполнять тело запроса GET, но у них, похоже, другое мнение, и они должны знать, что делают. astic.co/guide/en/elasticsearch/guide/current/…
GordonM
26
@iwein, указывающий значения тел запроса GET, на самом деле не является нарушением спецификации. HTTP / 1.1 указывает, что серверам СЛЕДУЕТ игнорировать тело, но RFC 2119 указывает, что разработчикам разрешено игнорировать предложения «СЛЕДУЕТ», если у них есть для этого веские основания. Скорее всего , клиент делает нарушает спецификацию , если он предполагает , что изменение тела GET будет не изменит ответ.
Эмиль Лундберг
108
RFC2616, называемый «HTTP / 1.1 spec», теперь устарел. В 2014 году он был заменен RFC 7230-7237. Цитата " тело сообщения ДОЛЖНО игнорироваться при обработке запроса " была удалена . Теперь это просто « Запрос на создание кадра не зависит от семантики метода, даже если метод не определяет никакого использования для тела сообщения ». Вторая цитата: « Метод GET означает получение любой информации ... идентифицированной по Request-URI » был удален . Итак, предлагаю отредактировать ответ @Jarl
Артем Наконечный
29
Я знаю, что это старая нить - я наткнулся на нее. @Artem Nakonechny технически прав, но новая спецификация гласит: «Полезная нагрузка в сообщении запроса GET не имеет определенной семантики; отправка тела полезной нагрузки по запросу GET может привести к тому, что некоторые существующие реализации отклонят запрос». Так что это все еще не очень хорошая идея, если ее можно избежать.
fastcatch
290

Хотя вы можете сделать это, поскольку это явно не исключено спецификацией HTTP, я бы посоветовал избегать этого просто потому, что люди не ожидают, что все будет работать именно так. В цепочке HTTP-запросов есть много этапов, и хотя они «в основном» соответствуют спецификации HTTP, единственное, что вы уверены, это то, что они будут вести себя так, как традиционно используются веб-браузерами. (Я имею в виду такие вещи, как прозрачные прокси, ускорители, A / V инструментарий и т. Д.)

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

Однако, если у вас есть веская причина, сделайте это.

Каски
источник
229
Принцип робастности ошибочен. Если вы будете либеральны в том, что вы принимаете, вы получите дерьмо, если у вас будет какой-либо успех с точки зрения усыновления, только потому, что вы принимаете дерьмо. Это усложнит вам развитие вашего интерфейса. Просто посмотрите на HTML. Это принцип ребустности в действии.
Евгений Бересовский
28
Я думаю, что успех и широта принятия (и злоупотребления) протоколов говорит о ценности принципа надежности.
Caskey
39
Вы когда-нибудь пытались разобрать настоящий HTML? Самостоятельно реализовать это невозможно, поэтому почти все, включая действительно крупных игроков, таких как Google (Chrome) и Apple (Safari), не делали этого, а полагались на существующие реализации (в конце концов, все они полагались на KHTML KDE). Такое повторное использование, конечно, хорошо, но вы пытались отобразить html в приложении .net? Это кошмар, так как вам нужно либо встраивать компонент - неуправляемый - IE (или аналогичный) с его проблемами и сбоями, либо вы используете доступный (в codeplex) управляемый компонент, который даже не позволяет выбирать текст.
Евгений Бересовский
6
Спецификация HTTP не только разрешает данные тела с помощью запроса GET, но это также распространенная практика: API _search популярного механизма ElasticSearch рекомендует запросы GET с запросом, присоединенным в теле JSON. В качестве уступки неполным реализациям HTTP-клиентов здесь также разрешены запросы POST.
Кристиан Питч
4
@ChristianPietsch, сегодня это обычная практика. Четыре года назад этого не было. Хотя спецификация явно позволяет клиенту необязательно включать (МОЖЕТ) объект в запрос (раздел 7), значение MAY определено в RFC2119, и прокси-сервер (дрянной) может быть совместимым со спецификацией при удалении объектов в запросах GET, в частности, до тех пор, пока не произойдет сбой, он может предоставить «ограниченную функциональность» путем пересылки заголовков запросов, а не включенной сущности. Аналогично, существует множество правил о том, какие изменения версий ДОЛЖНЫ / МОГУТ / СЛЕДУЕТ вносить при проксировании между различными уровнями протокола.
caskey
151

Скорее всего, вы столкнетесь с проблемами, если когда-нибудь попробуете воспользоваться кешированием. Прокси не будут искать в теле GET, чтобы увидеть, влияют ли параметры на ответ.

Даррел Миллер
источник
10
Использование полей заголовка ETag / Last-Modified помогает в этом: при использовании «условного GET» прокси / кэши могут действовать на эту информацию.
2010 года
2
@jldupont Кэши используют наличие валидаторов, чтобы знать, можно ли повторно подтвердить устаревший ответ, однако они не используются как часть первичного или вторичного ключа кэша.
Даррел Миллер
Это можно исправить с помощью контрольной суммы тела в параметре запроса
Адриан,
73

Ни консоль restclient, ни консоль REST не поддерживают это, но curl поддерживает.

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

Тело сообщения НЕ ДОЛЖНО быть включено в запрос, если спецификация метода запроса (раздел 5.1.1) не позволяет отправлять тело объекта в запросах.

Раздел 5.1.1 перенаправляет нас в раздел 9.x для различных методов. Ни один из них явно не запрещает включение тела сообщения. Однако...

Раздел 5.2 говорит

Точный ресурс, идентифицируемый интернет-запросом, определяется путем изучения URI-запроса и поля заголовка хоста.

и раздел 9.3 говорит

Метод GET означает получение любой информации (в форме объекта), идентифицируемой посредством Request-URI.

Что вместе указывает на то, что при обработке запроса GET серверу не требуется проверять что-либо, кроме поля заголовка Request-URI и хоста.

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

Дейв Дурбин
источник
2
Paw также имеет опцию для поддержки запросов GET с телами, но она должна быть включена в настройках.
s.Daniel
«Метод GET означает получение любой информации (в форме объекта), идентифицируемой посредством Request-URI». Тогда, технически незаконно / неправильно иметь конечную точку GET, которая получает все сущности? Например, GET /contacts/100/addressesвозвращает коллекцию адресов для человека с id=100.
Джош М.
Успокоенная библиотека Java для тестирования API REST не поддерживает GET-запрос с телом. Apache HttpClient также не поддерживает его.
Пауло Мерсон
Джанго также поддерживает разбор GET-тела
Bruno Finger
60

Elasticsearch принимает запросы GET с телом. Даже кажется, что это предпочтительный способ: Elasticsearch guide

Некоторые клиентские библиотеки (например, драйвер Ruby) могут регистрировать команду cry в stdout в режиме разработки, и он широко использует этот синтаксис.

jlecour
источник
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.
s.Daniel
11
Именно чтение документации по
упругому поиску привело
1
Это даже не должен быть сложный запрос. Даже простая прокрутка может вернуть очень длинный scroll_id (в кластере с большим количеством шардов), который приведет к превышению максимальной длины URL, если он там будет добавлен.
Брент Хроник
11
Elasticsearch поддерживает тот же запрос, используя POST. Они только решили разрешить тело в GET, потому что они чувствовали, что GET более семантически корректен, чем POST, когда дело доходит до запроса данных. Забавно, что Elasticsearch так много упоминается в этой теме. Я не стал бы использовать один пример (хотя и из популярного продукта) в качестве причины для следования практике.
DSO
33

То, чего вы пытаетесь достичь, уже давно было сделано с помощью гораздо более распространенного метода, который не использует полезную нагрузку с GET.

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

Это имеет преимущество в следовании традиционному методу PRG, помогает кеширующим посредникам кэшировать результаты и т. Д.

Тем не менее, URI в любом случае кодируются для всего, что не является ASCII, как и application / x-www-form-urlencoded и multipart / form-data. Я бы рекомендовал использовать это, а не создавать еще один пользовательский формат json, если вы намерены поддерживать сценарии ReSTful.

SerialSeb
источник
4
Вы можете просто создать свой конкретный поисковый медиатип. Не могли бы вы уточнить?
Петр Доброгост
2
При этом я говорил, что вы можете создать медиа-тип с именем application / vnd.myCompany.search + json, который будет содержать тип поискового шаблона, который вы хотите, чтобы клиент выдал, и клиент мог затем отправить его как POST. Как я уже подчеркивал, для этого уже существует тип мультимедиа, и он называется OpenSearch. Повторное использование существующего типа мультимедиа должно выбираться вместо пользовательского маршрута, когда вы можете реализовать свой сценарий с существующими стандартами.
SerialSeb
14
Это умно, но слишком сложно и неэффективно. Теперь вам нужно отправить POST с вашими критериями поиска, получить URI в качестве ответа от вашего POST, а затем отправить GET с URI критериев поиска на сервер для его получения GET критериев и отправить результат обратно вам. (За исключением того, что включение URI в URI технически невозможно, потому что вы не можете отправить что-то, которое может быть длиной до 255 символов, в то, что может быть не более 255 символов - поэтому вы должны использовать частичный идентификатор и ваш сервер, тогда нужно знать, как разрешить URI для ваших критериев поиска
POST
30

Вы можете отправить GET с телом или отправить POST и отказаться от RESTish религиозности (это не так уж и плохо, 5 лет назад в этой вере был только один член - его комментарии связаны выше).

Не являются хорошими решениями, но отправка тела GET может предотвратить проблемы для некоторых клиентов - и некоторых серверов.

Выполнение POST может иметь препятствия с некоторыми средами RESTish.

Джулиан Решке предложил выше использовать нестандартный HTTP-заголовок, такой как «SEARCH», который может быть элегантным решением, за исключением того, что он еще менее вероятно будет поддерживаться.

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

Клиенты, которые не могут отправить GET с телом (о котором я знаю):

  • XmlHTTPRequest Fiddler

Клиенты, которые могут отправить GET с телом:

  • большинство браузеров

Серверы и библиотеки, которые могут получить тело из GET:

  • апаш
  • PHP

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

  • ?
fijiaaron
источник
2
Squid 3.1.6 также удаляет тела GET, когда Content-Length равен 0 или не установлен, и в противном случае отправляет обратно HTTP 411 Length Required, даже если длина установлена
rkok
2
Скрипач будет, но он вас предупреждает.
toddmo
Вы говорите, что SEARCHметод может сломаться по пути? Если прокси не понимают метод, ожидается, что они пройдут через него как есть, поэтому я не слишком уверен, почему вы думаете, что это что-то сломает ...
Алексис Уилк
22

Какой сервер проигнорирует это? - fijiaaron 30 августа '12 в 21:27

Google, например, делает хуже, чем игнорирует его, он будет считать это ошибкой !

Попробуйте сами с помощью простого netcat:

$ 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-запросы с сущностью тела.

user941239
источник
67
Этот пример не имеет смысла, потому что, как правило, когда люди собираются добавлять тело к GETзапросам, это потому, что их собственный сервер может обработать это. Таким образом, вопрос заключается в том, будут ли другие «движущиеся части» (браузеры, кэши и т. Д.) Работать правильно.
Pacerier
6
Это плохие запросы, потому что ваша полезная нагрузка не ожидается (или не имеет смысла) для GET этой конкретной конечной точки - это не имеет ничего общего с использованием GETв общем случае. Случайная полезная нагрузка может POSTтак же легко прервать и вернуть то же самое 400 Bad Request, если содержимое не было в формате, который имел смысл в контексте конкретного запроса.
Нобар
И не только на этой конечной точке в целом, а скорее на этом конкретном URL .
Лоуренс Дол
1
Это не имеет значения, потому что это просто реализация сервера Google по этому URL. Так что нет смысла в вопросе
Джоэл Дакворт
для меня это было полезно, так как я пытался использовать функции firebase с запросом get + body, и эта ошибка может быть очень загадочной и трудной для понимания.
Scrimau
22

Я поставил этот вопрос в IETF HTTP WG. Комментарий Роя Филдинга (автора документа http / 1.1 в 1998 году) заключался в том, что

«... реализация будет нарушена для выполнения чего-либо, кроме анализа и удаления этого тела, если оно получено»

RFC 7213 (HTTPbis) заявляет:

«Полезная нагрузка в сообщении запроса GET не имеет определенной семантики;»

Теперь очевидно, что предполагалось, что семантическое значение в телах запросов GET запрещено, что означает, что тело запроса не может использоваться для воздействия на результат.

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

Итак, в заключение, не делайте этого.

Adrien
источник
19

Из RFC 2616, раздел 4.3 , «Тело сообщения»:

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

То есть серверы всегда должны читать любое предоставленное тело запроса из сети (проверьте Content-Length или прочитайте разделенное тело и т. Д.). Кроме того, прокси-серверы должны пересылать любое такое тело запроса, которое они получают. Затем, если RFC определяет семантику для тела для данного метода, сервер может фактически использовать тело запроса при генерации ответа. Однако, если RFC не определяет семантику для тела, то сервер должен игнорировать это.

Это соответствует цитате из Филдинга выше.

Раздел 9.3 , «GET», описывает семантику метода GET и не упоминает тела запросов. Следовательно, сервер должен игнорировать любое тело запроса, которое он получает по запросу GET.

izrik
источник
Раздел 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, установите для тела значение null.
  4. Если тело пусто, переходите к следующему шагу.

Хотя я не думаю, что так должно быть, потому что для запроса GET может потребоваться большой объем содержимого

Итак, если вы используете XMLHttpRequest браузера, скорее всего, он не будет работать.

Рафаэль Сэйлс
источник
1
понижен в связи с тем, что XMLHttpRequest является реализацией. Это может не отражать фактическую спецификацию, которую предполагается реализовать.
августа
10
Упомянутое выше неверно, если некоторые реализации не поддерживают отправку тела с GET, то это может быть причиной, по которой это не нужно делать, независимо от спецификации. Я действительно столкнулся с этой проблемой в кроссплатформенном продукте, над которым я работаю - только платформа, использующая XMLHttpRequest, не смогла отправить запрос get.
pjcard
8

Если вы действительно хотите отправить кэшируемое тело JSON / XML в веб-приложение, единственное разумное место для размещения ваших данных - это строка запроса, закодированная с помощью RFC4648: кодировка Base 64 с URL-адресом и безопасным алфавитом имени файла . Конечно, вы могли бы просто urlencode JSON и указать значение параметра URL, но Base64 дает меньший результат. Имейте в виду, что существуют ограничения размера URL-адреса, см. Какова максимальная длина URL-адреса в разных браузерах? ,

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

gertas
источник
Я думаю, что это довольно плохая идея :) Но если бы я сделал что-то подобное, я бы вместо этого использовал собственный HTTP-заголовок (и удостоверился, что я всегда отправляю обратно Vary: в ответе).
Evert
Плохо или нет, но выполнимо :) С данными в заголовке существует аналогичная проблема с размером данных, см. Stackoverflow.com/questions/686217/… . Однако спасибо за упоминание Varyзаголовка, я не знал о его реальном потенциале.
Gertas
6

Я бы не советовал это, это идет вразрез со стандартными методами и не предлагает так много взамен. Вы хотите сохранить тело для контента, а не вариантов.

cloudhead
источник
5

Как насчет несоответствующих закодированных base64 заголовков? "SOMETHINGAPP-Титулы: sdfSD45fdg45 / СОС"

Длина ограничения хм. Разве вы не можете сделать вашу обработку POST различать значения? Если вам нужны простые параметры, такие как сортировка, я не понимаю, почему это будет проблемой. Я думаю, это уверенность, что вы беспокоитесь.

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

Я расстроен, что REST как протокол не поддерживает ООП и 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
GSerjo
источник
8
Вы должны просто использовать POST. Если в URL-адресе есть имя метода, вы нарушаете основную схему остальных. Это RPC, используйте POST.
Эверт
3
Я не думаю, что это имеет большое значение, у нас больше проблем при разработке с RESTful url (т.е. orders / 1). Что касается меня, то что-то не так с методом Get, это несовместимо с ООП. И кого волнует, как выглядит url :) Но с помощью подхода, основанного на сообщениях, мы можем создать стабильный удаленный интерфейс, и это действительно важно. PS это не RPC, это основано на сообщениях
GSerjo
3
Я думаю, что вы упускаете весь смысл REST. Когда вы говорите, кто заботится о том, как выглядит URL, хорошо, REST заботится, очень. И почему REST совместим с ООП?
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

ИМХО, вы можете просто отправить JSONзакодированное (то есть. encodeURIComponent) В URL, таким образом, вы не нарушите HTTPспецификации и получите свой JSONна сервер.

EthraZa
источник
28
да, но главная проблема заключается в ограничении длины, как мы с этим справимся?
Себас
2

У вас есть список опций, которые намного лучше, чем использование тела запроса с GET.

Предположим, у вас есть категории и предметы для каждой категории. Оба идентифицируются по идентификатору ("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 с телом.

Xenonite
источник
0

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

Многие промежуточные инфраструктуры могут просто отклонять такие запросы.

К примеру, забудьте об использовании некоторых из доступных CDN в передней части вашего веб - сайта, как этот один :

Если GETзапрос средства просмотра включает тело, CloudFront возвращает код состояния HTTP 403 (Запрещено) для средства просмотра.

И да, ваши клиентские библиотеки также могут не поддерживать отправку таких запросов, как указано в этом комментарии .

Фредерик
источник
0

Я использую RestTemplate среды Spring в моей клиентской программе и на стороне сервера я определил запрос 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' содержит тело сообщения, предоставленное моим кодом. Я видел это через отладчик.

Однако из-за prepareConnection () getDoOutput () в executeInternal () всегда возвращает false, что, в свою очередь, делает bufferedOutput полностью игнорируемым! Он не копируется в выходной поток.

Следовательно, моя серверная программа не получила тела сообщения и вызвала это исключение.

Это пример шаблона RestTemplate среды Spring. Дело в том, что даже если тело сообщения больше не запрещено спецификацией HTTP, некоторые клиентские или серверные библиотеки или инфраструктуры могут по-прежнему соответствовать старой спецификации и отклонять тело сообщения из запроса GET.

Чжоу
источник
Вы не правильно читаете спецификацию или комментарии. Клиенты и серверы, отбрасывающие тело запроса, находятся в пределах спец. Не используйте GET тела запросов.
Эверт
@ Я не правильно прочитал комментарии или нет? :) Если вы прокрутите до ответа Пола Моргана (наиболее часто задаваемый ответ) и внимательно прочитаете комментарии, вы обнаружите следующее: «RFC2616, на который ссылаются как« HTTP / 1.1 spec », теперь устарел. В 2014 году его заменили на RFCs 7230-7237. Цитата «тело сообщения ДОЛЖНО игнорироваться при обработке запроса» была удалена. Теперь это просто «Кадр сообщения запроса не зависит от семантики метода, даже если метод не определяет какого-либо использования для тела сообщения ... "
Чжоу
@Evert Более того, я использовал утилиту тестирования REST «уверен», чтобы протестировать свой бэкэнд Spring-boot. И на сервере, и на Spring-boot сервер Json сохранял тело для запроса GET! Только RestTemplate Sping-framework удаляет тело из запросов GET, поэтому Spring-boot, rest-assured и RestTemplate, что является / не так?
Чжоу
@Evert Последнее, но не сдача в аренду, я не призывал людей использовать body в GET-запросах, напротив, я предлагал НЕ делать этого, анализируя исходный код RestTemplate Sping-фреймворка, так почему вы отказались от моего голосования? ответ?
Чжоу
Я не понизил ваш ответ. Я просто уточняю, что любая реализация HTTP, отбрасывающая GET-запрос, находится в спецификации.
Evert