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

Я ищу разумный способ представить поиск в качестве RESTful URLs.

настройка: у меня есть две модели, автомобили и гаражи, где автомобили могут быть в гаражах. Поэтому мои 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 design и для поиск.

[Update] мне нравится ответ Джастина, но он не охватывает случай многополевого поиска:

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

или что-то подобное. Как мы идем от

/cars/color/blue

в случае с несколькими полями?

12 ответов


для поиска используйте querystrings. Это совершенно успокаивает:

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

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


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

иерархической структуры

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

/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 является спокойной?**

список параметров

/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 соответствия для идеи сопоставления подстрок. Е. Г. поиск пользователей, содержащие слово "бар" user*=bar.)

вывод

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

так.. все эти символы разрешено без кода:

  • unreserved:a-zA-Z0-9_.-~
  • зарезервировано: ;/?:@=&$-_.+!*'(),
  • небезопасно*: <>"#%{}|\^~[]`

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

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

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

читать:
Иерархия:см. 2.3, см. 1.2.3
синтаксис параметра url path
атрибут помощью CSS3 соответствия
IBM: веб-службы RESTful - основы
Примечание: RFC 1738 был обновлен RFC 3986


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

  • не все символы, необходимые для поискового запроса, разрешены в URL-адресе. Большинство символов пунктуации и Юникода должны быть URL-адресом, закодированным в качестве параметра строки запроса. Я борюсь с той же проблемой. Я хотел бы использовать XPath в URL-адресе, но не весь синтаксис XPath совместим с путем URI. Так что для простых путей, /cars/doors/driver/lock/combination будет целесообразно найти 'combination' элемент в XML-документе двери водителя. Но!--2--> не так дружелюбно.

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

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

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

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

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

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

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

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


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

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)

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

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

идея объясняется более подробно в этой Railscast.


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

/search/{searchQuery}

или

/search/{savedSearchName}

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

посмотреть этот статьи изобретателем REST.


Я использую два подхода к реализации поиска.

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

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

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

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


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

Как я вижу, вы могли бы встроить его в то, как вы обрабатываете конкретные ресурсы:
/ автомобили / cam*

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

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


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

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

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


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

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


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

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

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


мой совет был бы такой:

/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 :) )

Edit:

/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-трафика.

вот ссылка на страницу, описывающую пороки строк запроса в Отдых: 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