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

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

395 голосов
/ 16 октября 2008

Я ищу разумный способ представления поисковых запросов в виде 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

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

Ответы [ 12 ]

408 голосов
/ 04 июля 2009

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

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

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

111 голосов
/ 15 марта 2013

RESTful красивый дизайн URL предназначен для отображения ресурса на основе структуры (структура, похожая на каталог, дата: 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_.-~
  • зарезервировано: ;/?:@=&$-_.+!*'(),
  • небезопасно *: <>"#%{}|\^~[]`

* Почему небезопасно и почему лучше кодировать: RFC 1738 см. 2.2

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

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

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

35 голосов
/ 29 мая 2009

Хотя наличие параметров в пути имеет некоторые преимущества, у 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 кажется громоздким.

30 голосов
/ 29 мая 2009

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

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 .

11 голосов
/ 16 октября 2008

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

/search/{searchQuery}

или

/search/{savedSearchName}
5 голосов
/ 10 октября 2014

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

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 голосов
/ 20 июля 2009

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

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

1 голос
/ 13 января 2016

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

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

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

1 голос
/ 06 октября 2014

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

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

1 голос
/ 04 марта 2009

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

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

Или вы можете просто добавить его в фильтр:
/ Автомобили / двери / 4 / имя / кулачковые * / цвет / красный, синий, зеленый

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

...