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?

Коротше кажучи, я шукаю вказівки для дизайну URL-адреси REST та для пошуку.

[Оновлення] Мені подобається відповідь Джастіна, але він не охоплює випадок пошуку в декількох полях:

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

чи щось подібне. Як ми йдемо

/cars/color/blue

до випадку з кількома полями?


16
Хоча це краще виглядає англійською мовою, змішування /carsі /carне є семантичним, а тому поганою ідеєю. Завжди використовуйте множину, коли під цією категорією є більше одного елемента.
Заз

4
Це погані відповіді. Для пошуку слід використовувати рядки запиту. При правильному використанні (тобто для пошуку) рядки запитів на 100% ВИПУСКОВІ.
pbreitenbach

Відповіді:


435

Для пошуку використовуйте запити. Це ідеально ВІДПОВІДНО:

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

Перевагою звичайних запитів є те, що вони є стандартними та широко зрозумілими, і що їх можна генерувати з форм-отримання.


42
Це вірно. Вся суть рядків запитів полягає у виконанні таких завдань, як пошук.
aehlke

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

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 стосується відображення ресурсу на основі структури (структура, схожа на каталог, дата: статті / 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 для ідеї відповідності підрядків. Наприклад, пошук користувачів, що містять "бар" user*=bar.)

Висновок

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

Отже .., усі ці символи дозволено незашифровано :

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

    Також див. RFC 1738 # page-20 для додаткових класів символів.

2.2
Незважаючи на те, що я раніше говорив, тут є загальне розмежування деліметрів, тобто деякі " важливіші", ніж інші.

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

Більше читання:
Ієрархія: див. 2.3 , див. 1.2.3
синтаксис параметра синтаксису параметра URL-адреси URL-адреси, що
відповідає
IBM: RESTful Web Services - Основи
Примітка: RFC 1738 було оновлено RFC 3986


3
Я не вірю, що не думав використовувати JSON у рядку запиту. Це відповідь на проблему, з якою я стикався - складна структура пошуку без використання POST. Крім того, інші ідеї, які ви дали у своїй відповіді, також дуже вдячні. Дуже дякую!
gustavohenke

4
@Qwerty: чудовий пост! Мені було цікаво: єдина причина використання ;на відміну від &читабельності? Тому що якщо так, то я думаю, що я б фактично віддав перевагу тому &, що це звичайніший роздільник ... так? :) Дякую!
Flo

3
@Flo Так точно :), але майте на увазі, що &розмежувач відомий лише розробникам. Батьки, бабусі та дідусі та не освічене населення приймають роздільники, як вони використовуються в загальному письмовому тексті.
Qwerty

17
Навіщо складати нестандартну схему, коли рядки запитів добре зрозумілі та стандартні?
pbreitenbach

1
@Qwerty нічого не зупиняє вас від / пошуку? Машини = червоний, синій, зелений та гаражі = 1,2,3 Або якщо ви використовуєте форму <multiselect>: / пошук? Автомобілі = червоні та автомобілі = сині та гаражі = 1 & гаражі = 2
pbreitenbach

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
так, я згоден ... окрім того, що я вимагає, щоб структура маршруту повинна відображати природні відносини між сутностями, якась карта моїх ресурсів, наприклад, у гаражі є багато машин, машина належить до гаража і так далі ... і нехай параметри фільтра, тому що це те, про що ми говоримо, для запиту рядків запитів ... що ви думаєте?
відкриваєтьсяко

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 або будь-якому іншому форматі. Як і для автомобіля та гаража, ви можете обмежити доступ до Пошуку на основі аутентифікації. Користувачі, які часто виконують одні й ті самі пошукові запити, можуть зберігати їх у своїх профілях, щоб їх не потрібно було створювати заново. URL-адреси будуть досить короткими, що у багатьох випадках їх можна легко торгувати електронною поштою. Ці збережені пошукові запити можуть бути основою користувацьких RSS-каналів тощо.

Існує багато можливостей використання пошукових запитів, коли ви думаєте про них як про ресурси.

Ідея більш докладно пояснена в цьому Railscast .


6
чи не підходить такий підхід ідеї роботи з неспокійним протоколом? Я маю на увазі, що наполегливий пошук на db - це щось на зразок зв’язкового зв'язку ... чи не так?
відкривається

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 як щось прогресивно прийнятне.
Jules Randolph

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

1

Хоча мені подобається відповідь Джастіна, я вважаю, що вона точніше представляє фільтр, а не пошук. Що робити, якщо я хочу знати про автомобілі з назвами, які починаються з кулачка?

Як я це бачу, ви можете вбудувати його у спосіб обробки конкретних ресурсів:
/ автомобілі / кулачок *

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

Особисто я віддаю перевагу останньому, однак я аж ніяк не експерт з REST (вперше почув про це лише 2 тижні тому) ...


/cars?name=cam*
Ось так

1

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

Ви можете пройти http://saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices/, щоб зрозуміти кращі методи розробки API RESTful API


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.

Сподіваємось, це дає вам ідею. По суті, ваш 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 для включення API "RESTful" здається небезпечним і короткозорим - тим більше, що вам доведеться писати власні складні відображення просто для обробки різних перестановок параметрів на URI. А ще краще, використовуйте вже існуюче поняття про використання крапки з комою у своїх URI: doriantaylor.com/policy/http-url-path-parameter-syntax
Анатолій Г
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.