Подтвердить что ты не робот

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

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

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

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

Мои вопросы:

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

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

4b9b3361

Ответ 1

Комментарий Роя Филдинга о включении тела с запросом 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" был удален - из комментария

Ответ 2

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

Это дух Принцип твердости примерно "либерально в том, что вы принимаете, и консервативен в том, что вы отправляете", я хочу без труда обосновать границы спецификации.

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

Ответ 3

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

Ответ 4

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

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

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

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

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

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

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

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

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

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

Ответ 5

Elasticsearch принимает запросы GET с телом. Кажется, что это предпочтительный вариант: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/common-options.html#_request_body_in_query_string

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

Ответ 6

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

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

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

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

Ответ 7

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

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

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

Julian Reschke предложил выше, используя нестандартный HTTP-заголовок, такой как "ПОИСК", который может быть изящным решением, за исключением того, что он даже менее вероятно будет поддерживаться.

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

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

  • XmlHTTPRequest Fiddler

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

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

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

  • Apache
  • PHP

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

Ответ 8

Какой сервер будет игнорировать его? - fijiaaron Aug 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 Bad Request от Bing, Apple и т.д., которые обслуживаются AkamaiGhost.

Поэтому я бы не советовал использовать запросы GET с сущностью тела.

Ответ 9

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

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

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

Это соответствует приведенной выше цитате из Fielding.

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

Ответ 10

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

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

RFC 7213 (HTTPbis) гласит:

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

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

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

Итак, вкратце, не делайте этого.

Ответ 11

Согласно XMLHttpRequest, это недействительно. Из стандарта:

4.5.6 Метод send()

client . send([body = null])

Инициирует запрос. Необязательный аргумент предоставляет тело запроса. Аргумент игнорируется, если метод запроса - GET или HEAD.

InvalidStateError исключение InvalidStateError если либо состояние не открыто, либо установлен флаг send().

Метод send(body) должен выполнить эти шаги:

  1. Если состояние не открыто, InvalidStateError исключение InvalidStateError.
  2. Если установлен флаг send(), InvalidStateError исключение InvalidStateError.
  3. Если метод запроса - GET или HEAD, установите для тела значение null.
  4. Если тело пусто, переходите к следующему шагу.

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

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

Ответ 12

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

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

Ответ 13

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

Ответ 14

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

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

Ответ 15

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

Ответ 16

Я расстроен тем, что 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

Ответ 17

Например, он работает с 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"}

Ответ 18

У вас есть список опций, которые намного лучше, чем использование тела запроса с 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 с телом.

Ответ 19

Google, IBM, Microsoft и apigee, похоже, используют заголовок для обозначения действительно ожидаемого метода, что-то вроде X-HTTP-Method-Override: GET.
Согласно вышеприведенному блогу, это введено для POST как PUT в форме.
Я думаю, что это решение может быть использовано в этой проблеме.

Другими словами, я думаю, что POST с X-HTTP-методом-Override: заголовок GET - лучшее решение.

Ответ 20

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

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

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

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

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