RESTful URL дизайн для поиска


427

Я ищу разумный способ представления поисковых запросов в виде RESTful URL.

Установка: у меня есть две модели, Автомобили и Гаражи, где Автомобили могут быть в Гаражах. Так что мои URL выглядят так:

/car/xxxx
  xxx == car id
  returns car with given id

/garage/yyy
  yyy = garage id
  returns garage with given id

Автомобиль может существовать сам по себе (отсюда и / автомобиль) или в гараже. Как правильно представить, скажем, все машины в данном гараже? Что-то вроде:

/garage/yyy/cars     ?

Как насчет объединения автомобилей в гараже yyy и zzz?

Как правильно представить поиск автомобилей с определенными атрибутами? Скажи: покажи мне все синие седаны с 4 дверями:

/car/search?color=blue&type=sedan&doors=4

или это должно быть / автомобили вместо?

Использование «поиска» там неуместно - что лучше / термин? Должно ли это быть просто:

/cars/?color=blue&type=sedan&doors=4

Должны ли параметры поиска быть частью PATHINFO или QUERYSTRING?

Короче говоря, я ищу руководство для кросс-модели REST URL дизайн и для поиска.

[Обновление] Мне нравится ответ Джастина, но он не охватывает случай поиска в нескольких полях:

/cars/color:blue/type:sedan/doors:4

или что-то типа того. Как мы идем из

/cars/color/blue

в случае нескольких полей?


16
Хотя это выглядит лучше на английском языке, смешивание /carsи /carне является семантическим и, следовательно, плохая идея. Всегда используйте множественное число, если в этой категории более одного элемента.
Zaz

4
Это плохие ответы. Поиск должен использовать строки запроса. Строки запроса на 100% RESTful при правильном использовании (т. Е. Для поиска).
pbreitenbach

Ответы:


435

Для поиска используйте строки запроса. Это совершенно ОТЛИЧНО:

/cars?color=blue&type=sedan&doors=4

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


42
Это правильно. Весь смысл строк запроса заключается в выполнении таких вещей, как поиск.
aehlke

22
Действительно, это правильно, так как, согласно RFC3986 , путь и строка запроса идентифицируют ресурс. Более того, правильное именование будет просто /cars?color=whatever.
Lloeki

35
Как насчет случаев, когда вам нужны компараторы (>, <, <=,> =)? / автомобили рейтинг <= 3?
Джесси

3
Что если вы хотите получить доступ к ресурсам, вложенным в строку запроса? Например /cars?color=blue&type=sedan&doors=4/engines, не будет работать
Abe Voelker

9
@mjs /cars?param=valueпредназначен для простой фильтрации по списку автомобилей и /cars/search?param=valueдля создания поиска (без сохранения), где результат может содержать оценку поиска, категоризацию и т. д. Вы также можете создать / удалить именованный поиск, например /cars/search/mysearch. Посмотрите на это: stackoverflow.com/a/18933902/1480391
Ив М.

121

Дизайн привлекательного URL RESTful предназначен для отображения ресурса на основе структуры (структура, похожая на директорию, дата: article / 2005/5/13, объект и его атрибуты, ..), косая черта /указывает на иерархическую структуру, используйте -idвзамен.

Иерархическая структура

Я бы лично предпочел:

/garage-id/cars/car-id
/cars/car-id   #for cars not in garages

Если пользователь удаляет /car-idчасть, это делает carsпредварительный просмотр интуитивно понятным. Пользователь точно знает, где в дереве он находится, на что он смотрит. С первого взгляда он знает, что гаражи и машины взаимосвязаны. /car-idтакже означает, что он принадлежит вместе в отличие от /car/id.

Поиск

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

/cars?color=blue;type=sedan   #most prefered by me
/cars;color-blue+doors-4+type-sedan   #looks good when using car-id
/cars?color=blue&doors=4&type=sedan   #I don't recommend using &*

Или в основном что-нибудь, что не является косой чертой, как объяснено выше.
Формула: /cars[?;]color[=-:]blue[,;+&]*, хотя я бы не использовал &знак, так как он неузнаваем по тексту на первый взгляд.

** Знаете ли вы, что передача объекта JSON в URI является RESTful? **

Списки вариантов

/cars?color=black,blue,red;doors=3,5;type=sedan   #most prefered by me
/cars?color:black:blue:red;doors:3:5;type:sedan
/cars?color(black,blue,red);doors(3,5);type(sedan)   #does not look bad at all
/cars?color:(black,blue,red);doors:(3,5);type:sedan   #little difference

возможные особенности?

(!) Нивелируют строки поиска
Для поиска любых автомобилей, но не черный и красный :
?color=!black,!red
color:(!black,!red)

Регистрация поиск
Поиск красные или синие или черные машины с 3 -х дверей в гаражах ID 1..20 или 101..103 или 999 , но не 5 /garage[id=1-20,101-103,999,!5]/cars[color=red,blue,black;doors=3]
Вы можете построить более сложные поисковые запросы. (Посмотрите на соответствие атрибутов CSS3 для идеи соответствия подстрок. Например, поиск пользователей, содержащих "bar" user*=bar.)

Вывод

Во всяком случае, это может быть самая важная часть для вас, потому что вы можете сделать это , однако вы , как в конце концов, просто имейте в виду , что RESTful URI представляет собой структуру , которая легко понять , например , каталогов , как /directory/file, /collection/node/item, даты /articles/{year}/{month}/{day}.. И когда вы опускаете любой из последних сегментов, вы сразу знаете, что вы получаете.

Итак, все эти символы разрешены в незашифрованном виде :

  • незарезервировано: как a-zA-Z0-9_.-~
    правило, разрешено как закодировано, так и нет, оба варианта использования эквивалентны.
  • специальные символы: $-_.+!*'(),
  • зарезервировано: ;/?:@=&
    может использоваться в незашифрованном виде для целей, которые они представляют, в противном случае они должны быть закодированы.
  • unsafe: <>"#%{}|\^~[]`
    почему небезопасно и почему лучше кодировать: RFC 1738 см. 2.2

    Также см. RFC 1738 # page-20 для большего количества классов символов.

RFC 3986 см. 2.2.
Несмотря на то, что я ранее сказал, здесь есть общее различие делимеров, означающее, что некоторые «являются» более важными, чем другие.

  • общие разделители: :/?#[]@
  • суб-разделители: !$&'()*+,;=

More reading:
Иерархия: см. 2.3 , см. 1.2.3
Синтаксис параметра пути CSS URL-
адреса, соответствующий атрибуту CSS3
IBM: Web-сервисы RESTful - Основы
Примечание: RFC 1738 был обновлен RFC 3986


3
Я не верю, что не думал об использовании JSON в строке запроса. Это ответ на проблему, с которой я столкнулся - сложная структура поиска без использования POST. Кроме того, другие идеи, которые вы дали в своем ответе, также весьма заметны. Огромное спасибо!
Густавохенке

4
@Qwerty: отличный пост! Мне было интересно: единственная причина использования ;в отличие от &читабельности? Потому что, если это так, я думаю, что на самом деле я бы предпочел, так &как это более распространенный разделитель ... верно? :) Спасибо!
Flo

3
@Flo Да точно :), но имейте ввиду, что &в качестве разделителя известен только разработчик. Родители, бабушки и дедушки и необразованное население принимает разделители в обычном письменном тексте.
Qwerty

17
Зачем создавать нестандартную схему, когда строки запроса хорошо понятны и стандартны?
pbreitenbach

1
@Qwerty ничто не мешает вам / search? Cars = red, blue, green & garages = 1,2,3 Или если вы используете форму <multiselect>: / search? Cars = red & cars = blue & garages = 1 &
garages

36

Хотя наличие параметров в пути имеет некоторые преимущества, IMO имеет некоторые перевешивающие факторы.

  • Не все символы, необходимые для поискового запроса, разрешены в URL. Большинство знаков препинания и Unicode должны быть URL-адресами в виде параметра строки запроса. Я борюсь с той же проблемой. Я хотел бы использовать XPath в URL, но не весь синтаксис XPath совместим с путем URI. Поэтому для простых путей /cars/doors/driver/lock/combinationбыло бы целесообразно найти combinationэлемент ' ' в XML-документе двери водителя. Но /car/doors[id='driver' and lock/combination='1234']не все так дружелюбно.

  • Существует разница между фильтрацией ресурса по одному из его атрибутов и указанием ресурса.

    Например, так как

    /cars/colors возвращает список всех цветов для всех автомобилей (возвращаемый ресурс является коллекцией цветовых объектов)

    /cars/colors/red,blue,green вернул бы список цветных объектов красного, синего или зеленого цвета, а не коллекцию автомобилей.

    Чтобы вернуть автомобили, путь будет

    /cars?color=red,blue,green или /cars/search?color=red,blue,green

  • Параметры в пути труднее читать, потому что пары имя / значение не изолированы от остальной части пути, которая не является парами имя / значение.

Последний комментарий Я предпочитаю /garages/yyy/cars(всегда во множественном числе) /garage/yyy/cars(возможно, это была опечатка в первоначальном ответе), потому что она избегает изменения пути между единственным и множественным числом. Для слов с добавленными 's' изменение не так уж плохо, но изменение /person/yyy/friendsна /people/yyyкажется громоздким.


2
да, я согласен ... кроме того, что структура URL пути должна отражать естественные отношения между сущностями, какая-то карта моих ресурсов, как в гараже много машин, машина принадлежит гаражу и так далее ... и пусть параметры фильтра, потому что это то, о чем мы говорим, к очереди запросов ... что вы думаете?
открывается

31

Чтобы расширить ответ Питера - вы можете сделать Поиск первоклассным ресурсом:

POST    /searches          # create a new search
GET     /searches          # list all searches (admin)
GET     /searches/{id}     # show the results of a previously-run search
DELETE  /searches/{id}     # delete a search (admin)

Ресурс поиска будет иметь поля для цвета, марки, модели, состояния гаража и т. Д. И может быть указан в XML, JSON или любом другом формате. Как и ресурс Car and Garage, вы можете ограничить доступ к поискам на основе аутентификации. Пользователи, которые часто запускают одни и те же поиски, могут сохранять их в своих профилях, чтобы их не нужно было заново создавать. URL-адреса будут достаточно короткими, чтобы во многих случаях их можно было легко обменять по электронной почте. Эти сохраненные поиски могут быть основой пользовательских RSS-каналов и т. Д.

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

Идея объяснена более подробно в этом Railscast .


6
Разве этот подход не противоречит идее работы с беспокойным протоколом? Я имею в виду, что поиск в БД - это своего рода соединение с состоянием ... не так ли?
открывается

5
Это больше похоже на обслуживание с сохранением состояния. Мы также меняем состояние сервиса каждый раз, когда добавляем новый Автомобиль или Гараж. Поиск - это просто еще один ресурс, который можно использовать с полным набором HTTP-глаголов.
Богатая Аподака

2
Как вышеупомянутое определяет соглашение URI?
Богатая Аподака

3
REST не имеет ничего общего с красивыми URI, вложенностью URI и т. Д. Если вы определяете URI как часть вашего API, это не REST.
aehlke

2
Я спорил об этом раньше. Это ни в коем случае не состояние, но это ужасная вещь. «Удаление» поиска не совсем понятно, здесь вы говорите, что оно удаляет эту поисковую сущность, но я бы хотел использовать ее, чтобы удалить результаты, которые я нашел в результате этого поиска. Пожалуйста, не добавляйте «поиски» в качестве ресурса.
Thecoshman

12

Ответ Джастина - это, вероятно, правильный путь, хотя в некоторых приложениях имеет смысл рассматривать конкретный поиск как самостоятельный ресурс, например, если вы хотите поддерживать именованные сохраненные поиски:

/search/{searchQuery}

или

/search/{savedSearchName}

11
нет. никогда не имеет смысла иметь действие быть ресурсом.
Thecoshman

3
@thecoshman, как упоминалось в комментарии выше, поиск также является существительным.
andho

6

Я использую два подхода для осуществления поиска.

1) Самый простой случай, для запроса связанных элементов и для навигации.

    /cars?q.garage.id.eq=1

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

Также возможно создание более сложных поисков:

    /cars?q.garage.street.eq=FirstStreet&q.color.ne=red&offset=300&max=100

Автомобили во всех гаражах FirstStreet, которые не являются красными (3-я страница, 100 элементов на страницу).

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

    POST /searches  => Create
    GET  /searches/1  => Recover search
    GET  /searches/1?offset=300&max=100  => pagination in search

Тело POST для создания поиска выглядит следующим образом:

    {  
       "$class":"test.Car",
       "$q":{
          "$eq" : { "color" : "red" },
          "garage" : {
             "$ne" : { "street" : "FirstStreet" }
          }
       }
    }

Он основан на Grails (критерии DSL): http://grails.org/doc/2.4.3/ref/Domain%20Classes/createCriteria.html


5

Это не ОТДЫХ. Вы не можете определить URI для ресурсов внутри вашего API. Навигация по ресурсам должна быть управляемой гипертекстом. Хорошо, если вам нужны красивые URI и большое количество соединений, но просто не называйте это REST, потому что это напрямую нарушает ограничения архитектуры RESTful.

Смотрите эту статью изобретателя REST.


28
Вы правы, что это не REST, это дизайн URL для системы RESTful. Вы также, однако, неверно утверждаете, что это нарушает архитектуру RESTful. Гипертекстовое ограничение REST ортогонально правильному дизайну URL для системы RESTful; Я помню, как несколько лет назад я обсуждал с Роем Т. Филдингом список REST, в котором я участвовал, и он так явно заявил об этом. Сказанный другим способом возможно иметь дизайн гипертекста и URL. Дизайн URL для систем RESTful подобен отступу в программировании; не требуется, но очень хорошая идея (игнорирование Python и т. д.)
MikeSchinkel

2
Извини, ты прав. У ОП только что сложилось впечатление, что он собирается рассказать клиентам о том, как создавать URL-адреса - он сделает «макеты» URL частью своего API. Это было бы нарушением REST.
aehlke

@aehlke, вы должны обновить свой ответ, чтобы он соответствовал вашему комментарию.
Джеймс МакМэхон

1
Модель соответствует уровню зрелости Ричардсона 2 уровня . Вы имеете в виду уровень 3. Просто примите REST как что-то прогрессирующее.
Жюль Рэндольф

1
@Jules Randolph - извинения, мой ответ был написан через несколько месяцев после того, как модель зрелости Ричардсона была впервые придумана, и до того, как Мартин Фаулер и другие авторы популяризировали ее :) Действительно, это поучительная модель для подражания. Не стесняйтесь редактировать ответ.
aehlke

1

Хотя мне нравится ответ Джастина, я чувствую, что он более точно представляет фильтр, а не поиск. Что если я захочу узнать об автомобилях с названиями, которые начинаются с cam?

На мой взгляд, вы можете встроить его в способ управления определенными ресурсами:
/ cars / cam *

Или просто добавить его в фильтр:
/ cars / doors / 4 / name / cam * / colors / red, синий, зеленый

Лично я предпочитаю последнее, однако я ни в коем случае не эксперт по REST (впервые услышав об этом всего 2 или около того недели назад ...)


Как это:/cars?name=cam*
DanMan

1

RESTful не рекомендует использовать глаголы в URL-адресах / cars / search, не успокаивая. Правильный способ фильтрации / поиска / разбивки на страницы вашего API - через параметры запроса. Однако могут быть случаи, когда вам приходится нарушать норму. Например, если вы осуществляете поиск по нескольким ресурсам, вам нужно использовать что-то вроде / search? Q = query

Вы можете перейти на страницу http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/, чтобы ознакомиться с лучшими методиками разработки API-интерфейсов RESTful.


1
Поиск тоже существительное 😀
jith912

1

Кроме того, я бы также предложил:

/cars/search/all{?color,model,year}
/cars/search/by-parameters{?color,model,year}
/cars/search/by-vendor{?vendor}

Здесь Searchрассматривается как дочерний ресурс Carsресурса.


1

Есть много хороших вариантов для вашего случая здесь. Тем не менее, вы должны рассмотреть возможность использования тела POST.

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

Это позволяет более гибкое описание поиска, а также позволяет избежать ограничения длины URL-адреса сервера.


-4

Мой совет будет таким:

/garages
  Returns list of garages (think JSON array here)
/garages/yyy
  Returns specific garage
/garage/yyy/cars
  Returns list of cars in garage
/garages/cars
  Returns list of all cars in all garages (may not be practical of course)
/cars
  Returns list of all cars
/cars/xxx
  Returns specific car
/cars/colors
  Returns lists of all posible colors for cars
/cars/colors/red,blue,green
  Returns list of cars of the specific colors (yes commas are allowed :) )

Редактировать:

/cars/colors/red,blue,green/doors/2
  Returns list of all red,blue, and green cars with 2 doors.
/cars/type/hatchback,coupe/colors/red,blue,green/
  Same idea as the above but a lil more intuitive.
/cars/colors/red,blue,green/doors/two-door,four-door
  All cars that are red, blue, green and have either two or four doors.

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

Вот ссылка на страницу, описывающую зло строк запросов в REST: http://web.archive.org/web/20070815111413/http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful

Я использовал кеш Google, потому что у меня не работала обычная страница, вот эта ссылка: http://rest.blueoxen.net/cgi-bin/wiki.pl?QueryStringsConsideredHarmful


1
Спасибо за подробный ответ. На последнем, что, если я хочу искать и по цвету и по количеству дверей? / автомобили / цвета / красный, синий, зеленый / двери / 4 Это не кажется правильным.
Парад

2
Запятые в URL мне не подходят, но все же действительный отдых. Я думаю, что это просто смена парадигмы.
Джастин Бозонье

21
Мне не нравится это предложение. Как бы вы узнали разницу между /cars/colors/red,blue,greenи /cars/colors/green,blue,red? Элемент пути URI должен быть иерархическим, и я действительно не вижу, чтобы это имело место здесь. Я думаю, что это ситуация, когда строка запроса является наиболее подходящим выбором.
troelskn

62
Это плохой ответ. Фактически, правильный способ осуществления поиска - это строки запроса. Строки запроса не являются злом ни в малейшей степени при правильном использовании. Приведенная статья не относится к поиску. Приведенные примеры явно подвергаются пыткам и не выдерживают больших параметров.
pbreitenbach

4
Строки запроса были созданы главным образом для решения проблемы запроса ресурса, даже с несколькими параметрами. Извращение URI для включения «RESTful» API кажется опасным и недальновидным, тем более что вам придется писать свои собственные сложные отображения только для обработки различных перестановок параметров в URI. Еще лучше, используйте уже существующее понятие использования точек с запятой в ваших URI: doriantaylor.com/policy/http-url-path-parameter-syntax
Anatoly G
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.