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

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

У меня есть некоторые веб-сервисы, которые я пишу, и я стараюсь быть как RESTful, насколько это возможно. Я размещаю эти веб-службы, используя HTTPHandler, работающий внутри IIS/ASP.NET/SharePoint.

Большинство моих служб ожидают HTTP GET. У меня есть два из них, которые просто возвращают некоторые данные (т.е. Запрос) и будут Idempotent, но параметры могут быть несколько сложный. Оба они могут включать символы в параметры службы, которые не разрешены, по крайней мере, для части PATH URL.

Используя IIS, ASP.NET и SharePoint, я обнаружил, что следующие символы в пути URL-адреса даже не попадают в мой HttpHandler, даже если Url закодирован (запрос взрывается, и у меня нет простого управления над этим):

  • % (% 25)
  • & (% 26)
  • * (% 2a, но не Url encode)
  • + (% 2b)
  • : (% 3a)
  • < (% 3c)
  • (% 3e)

Следующие символы попали в мой HttpHandler, но UriTemplate не смог обработать их правильно, даже если Url был закодирован:

  • (% 23)

  • . (% 2e, но не Url encode, UriTemplate удалил ".", Если is является последним символом перед /)
  • ? (% 3f)
  • /(% 2f - UriTemplate не работает по понятным причинам, даже если UrlEncoded)
  • \(% 5c)

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

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

Мой вопрос: какой вариант лучше? Вот некоторые из которых я знаю:

  • Использовать HTTP GET и строку запроса. Все, что может использовать специальные символы, должно быть в строке запроса и Url Encoded. Здесь я склоняюсь, но меня беспокоят чрезвычайно длинные строки запросов (IE имеет ограничение 2083)

  • Использовать кодировку HTTP GET и base64 в пути. Используйте Измененный Base64 для URL для любого параметры, которые могут использовать специальные символы и сохранять их как часть пути, если это необходимо. Я пробовал это, и он работает, но это отвратительно. Все еще беспокоит чрезвычайно длинные строки запросов.

  • Использовать HTTP POST и тело сообщения. Все, что может использовать специальные символы, должно быть в теле запроса. Похоже на достойное решение, но сообщения понимаются как не Idempotent и (я думал), как правило, предназначенный для изменений (тогда как здесь не происходит никаких изменений).

  • Использовать HTTP GET и тело сообщения. Все, что может использовать специальные символы, должно быть в теле запроса. Это выглядит как плохая идея в соответствии с SO: HTTP GET с телом запроса и Roy Fielding.

  • Используйте комбинацию # 3 и # 1 или # 2 выше в зависимости от того, насколько большой может быть запрос.

  • Другое???

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


Что касается длины URI, RFC2616 Sec3.2.1 говорит следующее:

Протокол HTTP не устанавливает априорного ограничения длины URI. Серверы ДОЛЖНЫ иметь возможность обрабатывать URI любого ресурса, который они обслуживают, и СЛЕДУЕТ иметь возможность обрабатывать URI неограниченной длины, если они предоставляют формы на основе GET, которые могут генерировать такие URI. Сервер ДОЛЖЕН возвращать 414 (Request-URI Too Long) статус, если URI больше, чем сервер может обрабатывать (см. Раздел 10.4.15).

  Note: Servers ought to be cautious about depending on URI lengths
  above 255 bytes, because some older client or proxy
  implementations might not properly support these lengths.

Кроме того, Максимальная длина URL-адреса составляет 2,083 символа в Internet Explorer.

Связанный: Как передать сложные запросы в REST?

4b9b3361

Ответ 1

Я рекомендую вам прочитать спецификацию HTTP 1.1, особенно разделы 3.2 Унифицированные идентификаторы ресурсов и 9.1.1 Безопасные методы. Надеюсь, они ответят на ваш вопрос.


Имеет дополнительную информацию:

Ответ 2

Нет идеального способа сделать это.

Правильный путь HTTP/REST - использовать GET и иметь все ваши параметры в URL как аргументы запроса. Вы определили две практические проблемы с этим подходом.

  • Ваше серверное программное обеспечение неправильно передает некоторые символы вам, даже если URL-адрес закодирован. Это меня действительно удивляет, и вы должны более внимательно изучить, что происходит, вы даже не можете получить% через URL. Предоставляет ли ваша структура необработанный доступ к PATH_INFO или другим необработанным символам? Это может дать вам обходной путь.
  • Строки запроса могут быть слишком длинными. Вы упомянете ограничение 2083 байта в MSIE. Это может быть или не быть практической проблемой для вас, в зависимости от того, является ли MSIE клиентом вашего API. (Т.е.: через Javascript делает вызовы API JSON). Но, по моему опыту, очень длинные URL-адреса в конечном итоге ломаются таинственно в нескольких местах; прокси-кэши по пути, даже брандмауэр с состоянием. Если у вас есть абсолютный контроль над клиентами и сетевой путь, вы, вероятно, можете жить с опасностью длинных URL-адресов. Если это общедоступный API, забудьте об этом.

Надеюсь, вы сможете сделать прямую работу GET в своей среде. Возможно, вам захочется рассмотреть возможность реорганизации вашего API, чтобы сделать данные запроса меньше.

Но что, если вы не можете заставить GET работать? Вы предлагаете несколько альтернатив. Я бы сразу уволил двух из них. Не помещайте контент в тело запроса GET; слишком много программного обеспечения сломается, если вы попробуете это, и, во всяком случае, это нарушает самый дух REST, который вы пытаетесь захватить. И я бы не использовал кодировку base64. Это может помочь вам решить проблему 1, ваш сервер не будет обрабатывать некоторые символы в URL-адресах. Но если применить неправильно, это фактически сделает ваши URL-адреса более длинными, а не короче, сложной проблемой. 2. Даже если вы правильно настроите base64 и включите некоторое сжатие, оно не сделает URL значительно короче и сделает клиента намного сложнее.

Самое практичное решение - вариант 3, HTTP POST. Это не RESTful; вы должны использовать GET для запросов только для чтения. И вы потеряете некоторые преимущества подхода REST с кэшированием GET и т.п. С другой стороны, он будет работать правильно и просто с большим разнообразием интернет-инфраструктуры и программных библиотек. Затем вы можете передать столько данных, которые хотите получить в теле POST, либо с помощью кодирования multipart/form-data, JSON, либо XML. (Я создал два основных общедоступных веб-сервиса, используя SOAP, который является просто XML на POST.Это уродливо, а не RESTful, но он работает надежно.)

REST - отличная парадигма дизайна. Это руководство. Если это не подходит вашему приложению, не чувствуйте, что вам нужно придерживаться его. HTTP не подходит для передачи больших объемов данных на сервер с помощью GET. Если вам нужны гигантские параметры запроса, сделайте что-нибудь еще.

Ответ 3

Если запрос слишком велик, чтобы перейти в URI, включите запрос в ресурс (например, сохраненный поиск). Я работал над остальным API для системы бронирования отелей; в поисковом запросе было слишком много параметров (предпочтения, список комнат... и т.д.), поэтому я превратил его в ресурс, который я отправляю на сервер. Затем сервер отвечает с URI, однозначно идентифицирующим поиск, тело которого представляет собой отправленный запрос + его результаты:

POST http://hotels.xyz/searches
body <search><query>...</query></search>

Ответ

201 Created - Location: http://hotels.xyz/searches/someID
Body <search><query>...</query><results>...</results></search>

Ответ 4

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

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

Ответ 5

Если вы создаете эти длинные URL-адреса на сервере, вы можете использовать сжатие для информации о пути.

Итак, если у вас есть что-то вроде /? param1 = bla-bla & param2 = bla-bla, вы просто сжимаете эти параметры и делаете URL-адрес похожим на:? query = ASsadcnfAFFASFscnsdlc

Когда вы получите такой запрос, вы просто распакуете их и проанализируете строку параметров

Ответ 6

Вы должны поместить параметры в строку запроса, используя запрос HTTP GET. Ограничения в некоторых старых веб-браузерах не являются проблемой, поскольку только пользователи, просматривающие API в веб-браузере, вероятно, будут разработчиками (или, по крайней мере, техническими). ​​

Помните, что клиентские приложения не должны манипулировать URL-адресами, которые предоставляет ваш API. URL-адреса являются непрозрачными идентификаторами для клиентов, которые используются только для направления их туда, где могут быть найдены определенные ресурсы.

Если по какой-либо причине это невозможно, я бы использовал запрос POST с параметрами, закодированными в тело. Он не будет полностью RESTful, но при условии, что ваши ресурсы правильно разработаны, влияние на клиентский код должно быть минимальным.

Ответ 7

Я бы определенно начал с того, с чего вы начали: сокращение URL. Я попытаюсь сократить имена параметров (? A = XXX; b = YYY; c = zzz); Reencode весь запрос к Base64; GZip Base64; Хаффман кодирует GZip;... все, что нужно. Как только я понял, что сокращение не будет работать для всех случаев (у вас есть какая-то динамическая система создания фильтров, которая может быть добавлена ​​неограниченно или без труда), то вы должны признать, что, возможно, пытаетесь сделать все в пределах одного запроса может не работать...

Я не собираюсь предлагать вам бросать несколько GET с разделенными параметрами и пытаться отслеживать запросы таким образом...

Единственный "надежный" метод, который я могу предложить, заключается в том, чтобы хранить/устанавливать запрошенную последовательность запросов в одном запросе (POST) и возвращать идентификатор фиксированного размера (или guid), который идентифицирует местоположение параметра запроса в вашем хранилище данных ( filterID), затем выполнить фактический запрос GET с использованием маркера filterID вместо полного значения строки запроса фильтра. Это позволит использовать всевозможные аккуратные вещи, такие как кеширование ответов на основе filterID, чтобы вы (в теории) повторно использовали одни и те же фильтры позже (вместо повторного ввода их вручную, просто сохраните "метку" вместе с телом фильтра и выберите из последние 5 фильтров по метке) или, по крайней мере, сохранить их в памяти с вашими данными, чтобы каждый раз, когда вы обновляете страницу, она не пересылает весь запрос фильтра.

Ответ 8

Рой Филдинг, скорее всего, одобрит использование POST в этой ситуации, но вы должны спросить его.

В целом, большинство приложений, которые включают в себя предоставленные пользователем данные, являющиеся поставляемые на сервер, небезопасны. Единственное исключение - когда информация находится в форме обобщенные параметры запроса для в котором существует компромисс между GET и POST, которые обычно включают в себя размер содержимого параметра. ПОЛУЧИТЬ является только желательным для этих случаев где параметры могут быть выражены как значимый URI.

Ответ 9

Я бы нацелился на HTTP POST. Он красиво обозначается, когда он попадает на PHP (или в зависимости от того, что вы используете), и у него нет ограничений по размеру, которые у других есть.

Ответ 10

base64 должен это сделать. другой разумно использовать стандартную% -значную.

Ответ 11

Рассмотрите поддержку:
- Запросы GET с короткой строкой запроса
- POST-запросы с длинной строкой запроса в тело и X-HTTP-метод-Override: GET (https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)

Не следует смешивать "POST/orders", который представляет собой массовое создание новых заказов и "POST/orders" с помощью "X-HTTP-Method-Override: GET", который является поиском порядка.