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

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

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

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

мои вопросы:

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

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

17 ответов


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

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

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

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

....Рой!--3-->

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

[...] если метод запроса не включает определенную семантику для сущности-тела, то тело сообщения должны игнорируется при обработке запроса.

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

метод GET означает получение любой информации ([...]) определяется Запрос URI.

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


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

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

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


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


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

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

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

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

5.2 говорит

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

и 9.3 говорит

метод GET означает получение любой информации (в виде сущности), идентифицируемой Запрос URI.

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

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


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

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


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

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

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

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


какой сервер будет игнорировать его? – 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-запросы с сущностью тела.


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

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

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

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

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

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

  • Запрос Скрипач

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

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

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

  • Apache
  • PHP

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

  • ?

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

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

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

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

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


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

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

RFC 7213 (HTTPbis) состояния:

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

теперь кажется ясным, что намерение было семантическим значение on GET request bodies запрещено, что означает, что тело запроса не может использоваться для влияния на результат.

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

Итак, в общем, не делайте этого.


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

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


Я расстроен, что REST как протокол не поддерживает ООП и Get метод доказательства. В качестве решения вы можете сериализовать свой DTO в JSON, а затем создать строку запроса. На стороне сервера вы сможете десериализовать строку запроса в DTO.

взгляните на:

сообщения может сделать ограничение метода. Вы сможете отправить любой DTO как с телом запроса

nelibur Web service framework предоставляет функциональные возможности, которые вы можете использовать

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

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

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


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


согласно XMLHttpRequest, это недопустимо. От стандартный:

4.5.6 в send() метод

client . send([body = null])

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

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

на send(body) метод должен выполнить следующие действия:

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

хотя, я не думаю, что это должен, потому что GET request может потребоваться большое содержание тела.

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


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


например, он работает с Curl, Apache и PHP.

PHP файл:

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