Дизайн URL-адреса RESTful для поиска
Я ищу разумный способ представления поисков как URL RESTful.
Настройка: у меня две модели, автомобили и гаражи, где автомобили могут находиться в гаражах. Поэтому мои 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
для случая с несколькими полями?
Ответы
Ответ 1
Для поиска, используйте querystrings. Это отлично RESTful:
/cars?color=blue&type=sedan&doors=4
Преимущество регулярных запросов состоит в том, что они являются стандартными и широко понятными и что они могут быть сгенерированы из формы-get.
Ответ 2
дизайн RESTful URL - это отображение ресурса на основе структуры (структура, подобная каталогу, дата: статьи /2005/5/13, объект и атрибуты,..), slash /
указывает иерархическую структуру, вместо этого используйте -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 для идеи подстроки. Например, поиск пользователей, содержащих "бар" user*=bar
.)
Заключение
В любом случае, это может быть для вас наиболее важной частью, потому что вы можете сделать это, как вам нравится, просто имейте в виду, что RESTful URI представляет собой структуру, которая легко понятна, например. /directory/file
, /collection/node/item
, даты /articles/{year}/{month}/{day}
. И когда вы опускаете какой-либо из последних сегментов, вы сразу узнаете, что вы получаете.
Итак, все эти символы разрешены unencoded:
- unreserved:
a-zA-Z0-9_.-~
- зарезервировано:
;/?:@=&$-_.+!*'(),
- небезопасно *:
<>"#%{}|\^~[]`
* Почему небезопасно и почему лучше кодировать: RFC 1738 см. 2.2
RFC 3986 см. 2.2
Несмотря на то, что я сказал ранее, здесь есть общее различие между метриками, что означает, что некоторые "являются" важнее других.
- общие разделители:
:/?#[]@
- sub-delimeters:
!$&'()*+,;=
Подробнее:
Иерархия: см. 2.3, см. 1.2.3
синтаксис параметра пути URL-адреса
соответствие атрибутов CSS3
IBM: веб-службы RESTful - основы
Примечание: RFC 1738 был обновлен RFC 3986
Ответ 3
Хотя параметры на пути имеют некоторые преимущества, существуют ИМО, некоторые факторы перевеса.
-
Не все символы, необходимые для поискового запроса, разрешены в URL-адресе. Большинство знаков препинания и символов Юникода должны быть закодированы в 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
кажется громоздким.
Ответ 4
Чтобы развернуть ответ Питера, вы можете сделать поиск первоклассным ресурсом:
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.
Ответ 5
Ответ Justin - это, вероятно, путь, хотя в некоторых приложениях может иметь смысл рассмотреть конкретный поиск как самостоятельный ресурс, например, если вы хотите поддерживать именованные сохраненные поисковые запросы:
/search/{searchQuery}
или
/search/{savedSearchName}
Ответ 6
Это не REST. Вы не можете определить URI для ресурсов внутри вашего API. Навигация по ресурсам должен управляться гипертекстом. Это хорошо, если вам нужны симпатичные URI и тяжелая связь, но просто не называйте это REST, потому что это напрямую нарушает ограничения архитектуры RESTful.
См. статью статьи изобретателем REST.
Ответ 7
Я использую два подхода для реализации поиска.
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
Ответ 8
Хотя мне нравится ответ Джастина, я чувствую, что он более точно представляет фильтр, а не поиск. Что, если я хочу знать о машинах с именами, которые начинаются с камеры?
Как я вижу это, вы можете создать его так, как вы обрабатываете определенные ресурсы:
/Автомобили/Ж *
Или вы можете просто добавить его в фильтр:
/Автомобили/двери/4/имя/кулачковые */цвет/красный, синий, зеленый
Лично я предпочитаю последнее, однако я ни в коем случае не специалист по REST (впервые услышав об этом всего 2 или около того недели назад...)
Ответ 9
RESTful не рекомендует использовать глаголы в URL/cars/search не успокаивается. Правильный способ фильтрации/поиска/разбиения на страницы вашего API осуществляется с помощью параметров запроса. Однако могут быть случаи, когда вам приходится нарушать норму. Например, если вы выполняете поиск по нескольким ресурсам, вы должны использовать что-то вроде поиска /q? Query =/
Вы можете пройти через http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/, чтобы понять лучшие методы разработки API RESTful API
Ответ 10
Кроме того, я бы также предложил:
/cars/search/all{?color,model,year}
/cars/search/by-parameters{?color,model,year}
/cars/search/by-vendor{?vendor}
Здесь Search
рассматривается как дочерний ресурс ресурса Cars
.
Ответ 11
Здесь есть много хороших вариантов для вашего дела. Тем не менее вам следует рассмотреть возможность использования тела POST.
Строка запроса идеально подходит для вашего примера, но если у вас есть что-то более сложное, например. произвольный длинный список элементов или логических условных выражений, вы можете определить пост в качестве документа, который клиент отправляет через POST.
Это позволяет более гибкое описание поиска, а также избегает ограничения длины URL-адреса сервера.
Ответ 12
Мой совет будет таким:
/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-трафика.
Здесь ссылка на страницу, описывающую пороки строк запроса в 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