Najważniejsze wskazówki dotyczące interfejsu API REST: gdzie umieszczać parametry? [Zamknięte]

348

Interfejs API REST może mieć parametry na co najmniej dwa sposoby:

  1. Jako część ścieżki URL (tj. /api/resource/parametervalue )
  2. Jako argument zapytania (tj. /api/resource?parameter=value )

Jaka jest tutaj najlepsza praktyka? Czy istnieją jakieś ogólne wytyczne, kiedy stosować 1, a kiedy 2?

Przykład ze świata rzeczywistego: Twitter używa parametrów zapytania do określania przedziałów. (http://api.twitter.com/1/statuses/home_timeline.json?since_id=12345&max_id=54321 )

Czy uznanie za lepsze zaprojektowanie umieszczenia tych parametrów w ścieżce adresu URL byłoby lepsze?

Kalle Gustafsson
źródło

Odpowiedzi:

254

Jeśli istnieją udokumentowane najlepsze praktyki, jeszcze ich nie znalazłem. Oto jednak kilka wskazówek, których używam przy określaniu, gdzie umieścić parametry w adresie URL:

Parametry opcjonalne są zwykle łatwiejsze do umieszczenia w ciągu zapytania.

Jeśli chcesz zwrócić błąd 404, gdy wartość parametru nie odpowiada istniejącemu zasobowi, wówczas dążyłbym do parametru segmentu ścieżki. np. /customer/232gdzie 232 nie jest prawidłowym identyfikatorem klienta.

Jeśli jednak chcesz zwrócić pustą listę, to gdy parametr nie zostanie znaleziony, sugeruję użycie parametrów ciągu zapytania. na przykład/contacts?name=dave

Jeśli parametr wpływa na całe poddrzewo przestrzeni URI, użyj segmentu ścieżki. np. parametr języka /en/document/foo.txt kontra/document/foo.txt?language=en

Wolę, aby unikalne identyfikatory znajdowały się w segmencie ścieżki niż w parametrze zapytania.

Oficjalne zasady dotyczące identyfikatorów URI można znaleźć w tej specyfikacji RFC tutaj . Istnieje również inna bardzo przydatna specyfikacja RFC , która definiuje reguły parametryzacji URI.

Darrel Miller
źródło
5
Oficjalne identyfikatory URI reguł i sepc wersji roboczej były naprawdę przydatne i interesujące! :-)
KajMagnus
1
Test błędu 404 bardzo pomaga mi uniknąć umieszczania informacji w ścieżce, która należy do parametrów zapytania, nagłówków lub treści żądania. Dzięki za zwrócenie na to uwagi!
Kevin Condon
152

Późna odpowiedź, ale dodam trochę więcej wglądu do tego, co zostało udostępnione, a mianowicie, że istnieje kilka rodzajów „parametrów” w żądaniu i należy to wziąć pod uwagę.

  1. Lokalizatory - np. Identyfikatory zasobów, takie jak identyfikatory lub akcja / widok
  2. Filtry - np. Parametry, które umożliwiają wyszukiwanie, sortowanie lub zawężanie zestawu wyników.
  3. Stan - np. Identyfikacja sesji, klucze API, whatevs.
  4. Treść - np. Dane, które mają być przechowywane.

Teraz spójrzmy na różne miejsca, w których mogą iść te parametry.

  1. Żądaj nagłówków i plików cookie
  2. Ciąg zapytania adresu URL (zmienne „GET”)
  3. Ścieżki URL
  4. Ciąg zapytania / treść wieloczęściowa (zmienne „POST”)

Zasadniczo chcesz, aby stan był ustawiony w nagłówkach lub plikach cookie, w zależności od rodzaju informacji o stanie. Myślę, że wszyscy możemy się z tym zgodzić. W razie potrzeby użyj niestandardowych nagłówków http (X-My-Header).

Podobnie, Treść ma tylko jedno miejsce, do którego należy przynależność, która znajduje się w treści żądania, jako ciągi zapytania lub jako wieloczęściowy http i / lub treść JSON. Jest to zgodne z tym, co otrzymujesz z serwera, gdy wysyła ci treść. Więc nie powinieneś być niegrzeczny i rób to inaczej.

Lokalizatory takie jak „id = 5” lub „action = refresh” lub „page = 2” powinny mieć postać ścieżki URL, na przykład, gdy mysite.com/article/5/page=2częściowo wiesz, co każda część powinna oznaczać (podstawy, takie jak artykuł i 5 oczywiście oznacza, że ​​podasz mi dane typu artykuł o id 5), a dodatkowe parametry są określone jako część identyfikatora URI. Mogą mieć postać page=2lub page/2jeśli wiesz, że po pewnym punkcie identyfikatora URI „foldery” są sparowanymi klucz-wartościami.

Filtry zawsze trafiają do ciągu zapytania, ponieważ chociaż są one częścią wyszukiwania właściwych danych, służą tylko do zwrócenia podzbioru lub modyfikacji tego, co zwracają Lokalizatory samodzielnie. Wyszukiwanie w mysite.com/article/?query=Obama(podzestawie) jest filtrem i tak samo jest/article/5?order=backwards (modyfikacja). Pomyśl o tym, co robi, a nie tylko jak się nazywa!

Jeśli „widok” określa format wyjściowy, to jest to filter ( mysite.com/article/5?view=pdf), ponieważ zwraca modyfikację znalezionego zasobu, a nie bazuje na tym, którego zasobu chcemy. Jeśli zamiast tego zdecyduje, którą konkretną część artykułu zobaczymy ( mysite.com/article/5/view=summary), to jest to lokalizator.

Pamiętaj, zawężenie zestawu zasobów jest filtrowaniem. Lokalizowanie czegoś konkretnego w zasobie to lokalizowanie ... duh. Filtrowanie podzbiorów może zwracać dowolną liczbę wyników (nawet 0). Lokalizowanie zawsze znajdzie określone wystąpienie czegoś (jeśli istnieje). Filtrowanie modyfikacji zwróci te same dane co lokalizator, z wyjątkiem zmodyfikowanych (jeśli taka modyfikacja jest dozwolona).

Mam nadzieję, że to pomogło dać ludziom chwile eureki, jeśli zgubili się, gdzie umieścić rzeczy!

Tor Valamo
źródło
2
Dlaczego więc nie idma filtra? Zwraca podzbiór zasobu
Jonathan.
13
@Jathanathan. nie, zwraca określony zasób, a mianowicie numer artykułu 5. Filtr jest zawsze sposobem zawężenia wyszukiwania w zbiorze zasobów. Jeśli chcesz tylko ten konkretny zasób, powinien istnieć wyznaczony sposób jego uzyskania. Filtrowanie oznacza, że ​​możesz zwrócić wiele zasobów. Identyfikator nie jest filtrem, to określony pojedynczy zasób. Gdybyś miał ZAKRES identyfikatorów, byłby to filtr, nawet jeśli zakres obejmowałby tylko jeden identyfikator. Jeśli filtr zawiera również typy zasobów, zwróci wszystkie zasoby o identyfikatorze 5, a nie tylko artykuł.
Tor Valamo
1
@Jathanathan .: jak wspomniano DarrelMiller, można oczekiwać, że obiekt / id zwróci 404 w przypadku nieznanego identyfikatora, podczas gdy można oczekiwać, że obiekt? Id = id zwróci i opróżni listę. Ponadto uważam, że każdy rodzaj filtrowania / podzbiorów powinien zwracać listę.
njzk2
1
Strony są trudne, ponieważ, jak mówisz, może to być filtr zasobu (zbiór stron), ale jednocześnie jest to konkretny zasób w tej kolekcji. Zawsze prosiłbym o stronę artykułu według lokalizatora, a nie filtrowania. Jednak strona może być filtrem listy czegoś, powiedzmy listy użytkowników. Ale wtedy strona jest z natury ogranicznikiem, czyli „zacznij od elementu (page-1)*perpagei pokaż perpageelementy”. Używanie go jako filtra jest poprawne, ale z różnych powodów. Nazywanie go „stroną” jest technicznie nieprawidłowe. Bardziej poprawne semantycznie byłoby nazwać to „od” lub „startAt”
Tor Valamo
1
(ciąg dalszy) Semantyczne znaczenie „strony” polega na tym, że jest to konkretny zasób, który się nie zmienia. Pochodzi z wydruku fizycznego. Gdybyśmy nigdy nie mieli książek ani druków, „strona” nie byłaby słowem. Jeśli masz dynamiczną listę elementów podzieloną na „strony”, naprawdę powinieneś podać konkretny punkt początkowy, liczbowy, alfabetyczny lub nawet specyficzny dla elementu, a także filtr „ile na stronę”. Jeśli chcę odwoływać się do czegoś na twojej liście, chcę specyfiki. Nie chcę też przechodzić do strony 5, aby zdać sobie sprawę, że zmieniłeś teraz wewnętrzny perpagena 50 zamiast na 20.
Tor Valamo
21

To zależy od projektu. Nie ma reguł dla identyfikatorów URI w REST przez HTTP (najważniejsze jest to, że są unikalne). Często chodzi o gust i intuicję ...

Przyjmuję następujące podejście:

  • url path-element: zasób i jego element path tworzą przejście do katalogu i podrzędne źródło (np. / items / {id}, / users / items). W razie wątpliwości zapytaj kolegów, czy myślą, że przejście i myślą w „innym katalogu”, najprawdopodobniej ścieżka jest właściwym wyborem
  • parametr url: gdy tak naprawdę nie ma przechodzenia (zasoby wyszukiwania z wieloma parametrami zapytania są tego bardzo dobrym przykładem)
Manuel Aldana
źródło
1
W rzeczywistości istnieją dość jasne zasady dotyczące tego, jak powinien wyglądać identyfikator URI, i bardzo mało niejasności co do tego, jak zastosować je do identyfikatorów URI RESTful.
DanMan
18

IMO parametry powinny być lepsze jako argumenty zapytania. Adres URL służy do identyfikowania zasobu, a dodane parametry zapytania określają, którą część zasobu chcesz, dowolny stan, który powinien mieć zasób itp.

PeterWong
źródło
7
W rzeczywistości zarówno ścieżka, jak i zapytanie są używane w połączeniu do identyfikacji zasobu. Zostało to wyjaśnione w RFC 3986 http://labs.apache.org/webarch/uri/rfc/rfc3986.html#query
Darrel Miller
@DarrelMiller Wiem, że to stary post, ale chciałbym dowiedzieć się więcej o tym, że parametry zapytania służą również do identyfikacji zasobu. Podany link jest już nieaktualny. Patrzyłem na RFC3986, ale nie rozumiem, jak wydedukowałeś ten fakt. Ponadto, z definicji, parametry identyfikatora nie powinny być opcjonalne, więc nie wydaje się właściwe stosowanie parametrów zapytania do identyfikacji.
Mickael Marrache
@MickaelMarrache Zobacz pierwszą linię w sekcji 3.4 tools.ietf.org/html/rfc3986#section-3.4
Darrel Miller
2
@DarrelMiller Thanks! Moje pytanie wynika z faktu, że pośredniczące komponenty HTTP zasadniczo nie buforują odpowiedzi na żądania zawierające ciąg zapytania. Wygląda więc na to, że parametry zapytania są lepiej dostosowane do wyszukiwania zasobów zgodnie z niektórymi kryteriami, a nie do jednoznacznej identyfikacji zasobu.
Mickael Marrache
17

Zgodnie z implementacją REST

1) Zmienne ścieżki służą do bezpośredniego działania na zasoby, takie jak kontakt lub utwór, np.
GET etc / api / resource / {songid} lub
GET etc / api / resource / { zwróci odpowiednie dane.

2) Zapytania perms / argument są używane dla zasobów bezpośrednich, takich jak metadane utworu, np. GET / api / resource / {songid}? Metadata = gatunki, zwróci dane gatunku dla tego konkretnego utworu.

Satish Bellapu
źródło
5
W rzeczywistości nie ma standardu REST . Według Wikipedii : W przeciwieństwie do usług internetowych opartych na SOAP, nie ma „oficjalnego” standardu interfejsów API RESTful. [14] Wynika to z faktu, że REST jest stylem architektonicznym, w przeciwieństwie do SOAP, który jest protokołem. Mimo że REST nie jest standardem, implementacja RESTful, taka jak Internet, może używać standardów takich jak HTTP, URI, XML itp.
DavidRR
Nie podoba mi się podejście 2. Wolałbym preferować / api / gatunki? Songid = 123 lub / api / piosenki / {song-id} / gatunki
Bart Calixto
1
@ Bart, Satish odnosił się do Zmiennych na ścieżce, co jest zasadniczo tym, co określiłeś jako swoje preferencje .. jeśli jednak gatunki są w rzeczywistości metadanymi, a nie polem podmiotu / zasobu utworu ... to mógłbym zobaczyć większą wrażliwość używając do tego ciągu zapytania.
Brett Caswell
@BrettCaswell ma to! dzięki za zwrócenie na to uwagi. naprawdę to doceniam!
Bart Calixto
16

„Spakuj” i POST swoje dane w kontekście „kontekstu”, który zapewnia lokalizator zasobów wszechświata, co oznacza numer 1 dla samego lokalizatora.

Zwróć uwagę na ograniczenia z # 2. Wolę testy POST niż nr 1.

Uwaga: ograniczenia są omówione dla

POST w Czy istnieje maksymalny rozmiar zawartości parametru POST?

GET w Czy istnieje limit długości żądania GET? i Maksymalny rozmiar parametrów URL w _GET

ps limity te są oparte na możliwościach klienta (przeglądarka) i serwera (konfiguracja).

dgm
źródło
dodatek: dowcipne trasy mogą mieć wersje (rozróżniane za pomocą nagłówków), dzięki czemu zapewniają rozwiniętą funkcjonalność bez potrzeby modyfikowania kodu, który zużywa kod wypełnienia (api), który piszesz jak w restify -> szukaj tras wersjonowanych
dgm
5

Zgodnie ze standardem URI ścieżka dotyczy parametrów hierarchicznych, a zapytanie dotyczy parametrów niehierarchicznych. Ofc. może być bardzo subiektywne, co jest dla ciebie hierarchiczne.

W sytuacjach, w których do tego samego zasobu przypisanych jest wiele identyfikatorów URI, lubię umieszczać parametry - niezbędne do identyfikacji - na ścieżce, a parametry - niezbędne do zbudowania reprezentacji - w zapytaniu. (Dla mnie w ten sposób łatwiej jest wyznaczyć trasę.)

Na przykład:

  • /users/123 i /users/123?fields="name, age"
  • /users i /users?name="John"&age=30

Do zmniejszania mapy lubię stosować następujące podejścia:

  • /users?name="John"&age=30
  • /users/name:John/age:30

Tak naprawdę to od Ciebie (i routera po stronie serwera) zależy, jak zbudujesz URI.

Uwaga: aby wspomnieć, te parametry są parametrami zapytania. Tak więc to, co naprawdę robisz, to zdefiniowanie prostego języka zapytań. Poprzez złożone zapytania (które zawierają operatory takie jak i, lub większe niż itp.) Sugeruję, abyś używał już istniejącego języka zapytań. Możliwości szablonów URI są bardzo ograniczone ...

inf3rno
źródło
4

Jako programista często po stronie klienta wolę argument zapytania. Również dla mnie oddziela ścieżkę URL od parametrów, dodaje przejrzystości i oferuje większą rozszerzalność. Pozwala mi to również na oddzielną logikę między budowaniem adresu URL / URI a konstruktorem parametrów.

Podoba mi się to, co powiedział Manuel Aldana na temat drugiej opcji, jeśli w grę wchodzi jakieś drzewo. Widzę, że części specyficzne dla użytkownika są usuwane w ten sposób.

Joe Plante
źródło
4

Nie ma twardych i szybkich reguł, ale ogólną zasadę z czysto koncepcyjnego punktu widzenia, którą lubię stosować, można krótko podsumować w następujący sposób: ścieżka URI (z definicji) reprezentuje zasób, a parametry zapytania są zasadniczo modyfikatorami tego zasobu . Tak daleko, że prawdopodobnie nie pomoże ... Z REST API masz główne metody działając na podstawie pojedynczego zasobu użyciu GET, PUToraz DELETE. Dlatego to, czy coś powinno być reprezentowane na ścieżce, czy jako parametr, można sprowadzić do tego, czy metody te mają sens dla danej reprezentacji. Czy byłbyś rozsądnie PUTna tej ścieżce i czy byłoby to semantycznie rozsądne? Mógłbyś oczywiściePUT coś dosłownie wszędzie i wygiąć zaplecze, aby sobie z tym poradzić, ale tak powinno byćPUTco stanowi reprezentację rzeczywistego zasobu, a nie jakiejś niepotrzebnie kontekstowej wersji tego zasobu. W przypadku kolekcji to samo można zrobić POST. Jeśli chcesz dodać do konkretnej kolekcji, jaki adres URL ma sens POST.

Nadal pozostawia to niektóre szare obszary, ponieważ niektóre ścieżki mogą wskazywać, ile dzieci zasobów rodzicielskich jest nieco uznaniowe i zależy od ich wykorzystania. Jedyną twardą linią, którą to rysuje, jest to, że każdy rodzaj przechodnich reprezentacji powinien być wykonywany przy użyciu parametru zapytania, ponieważ nie miałby zasobu podstawowego.

W odpowiedzi na przykład ze świata rzeczywistego podany w pierwotnym pytaniu (API Twittera) parametry reprezentują zapytanie przechodnie, które filtruje stan zasobów (a nie hierarchię). W tym konkretnym przykładzie byłoby całkowicie nierozsądne dodawanie do kolekcji reprezentowanej przez te ograniczenia, a ponadto to zapytanie nie byłoby reprezentowane jako ścieżka, która miałaby jakikolwiek sens w kategoriach grafu obiektowego.

Przyjęcie tego rodzaju perspektywy zorientowanej na zasoby może łatwo odwzorować bezpośrednio na wykresie obiektowym modelu domeny i doprowadzić logikę interfejsu API do punktu, w którym wszystko działa bardzo czysto i w sposób dość dokumentujący, gdy tylko osiągnie przejrzystość. Koncepcję tę można również wyjaśnić, odchodząc od systemów, które używają tradycyjnego routingu adresów URL odwzorowanego na normalnie źle dopasowany model danych (tj. RDBMS). Proca Apache z pewnością byłaby dobrym miejscem do rozpoczęcia. Koncepcja wysyłania obiektów w systemie takim jak Zope zapewnia również wyraźniejszy analog.

Matt Whipple
źródło
4

Oto moja opinia.

Parametry zapytania są używane jako metadane żądania. Działają one jako filtr lub modyfikator istniejącego wywołania zasobu.

Przykład:

/calendar/2014-08-08/events

powinien podać wydarzenia kalendarzowe na ten dzień.

Jeśli chcesz wydarzenia dla określonej kategorii

/calendar/2014-08-08/events?category=appointments

lub jeśli potrzebujesz wydarzeń trwających dłużej niż 30 minut

/calendar/2014-08-08/events?duration=30

Test lakmusa polegałby na sprawdzeniu, czy żądanie może być nadal obsługiwane bez parametrów zapytania.

Sójka
źródło
2

Zazwyczaj dążę do # 2, jako argument zapytania (tj. / Api / resource? Parametr = wartość).

Trzecią opcją jest opublikowanie parametru = wartość w treści.

Wynika to z faktu, że działa lepiej w przypadku zasobów o wielu parametrach i jest bardziej rozszerzalny do wykorzystania w przyszłości.

Bez względu na to, który wybierzesz, upewnij się, że wybierasz tylko jeden, nie mieszaj i nie dopasowuj. To prowadzi do mylącego API.

NorthIsUp
źródło
2

Jeden „wymiar” tego tematu został pominięty, ale jest on bardzo ważny: są chwile, w których „najlepsze praktyki” muszą być zgodne z planem, który wdrażamy lub rozszerzamy o możliwości REST.

Praktyczny przykład:

Wiele aplikacji internetowych obecnie implementuje architekturę MVC (Model, Widok, Kontroler). Zakładają, że zapewniona jest pewna standardowa ścieżka, tym bardziej, gdy te aplikacje internetowe mają opcję „Włącz adresy URL SEO”.

Wystarczy wspomnieć o dość znanej aplikacji internetowej: sklepie e-commerce OpenCart. Gdy administrator włączy „Adresy URL SEO”, oczekuje, że wspomniane adresy URL pojawią się w dość standardowym formacie MVC, takim jak:

http://www.domain.tld/special-offers/list-all?limit=25

Gdzie

  • special-offers to kontroler MVC, który przetwarza adres URL (pokazuje stronę z ofertami specjalnymi)

  • list-allto nazwa akcji lub funkcji kontrolera, którą należy wywołać. (*)

  • limit = 25 jest opcją, stwierdzającą, że na stronie będzie wyświetlanych 25 pozycji.

(*) list-allto fikcyjna nazwa funkcji, której użyłem dla jasności. W rzeczywistości OpenCart i większość platform MVC ma domyślną, domyślną (i zwykle pomijaną w adresie URL) indexfunkcję, która jest wywoływana, gdy użytkownik chce wykonać domyślną akcję. Tak więc rzeczywisty adres URL to:

http://www.domain.tld/special-offers?limit=25

Dzięki teraz dość standardowej aplikacji lub strukturze ramowej podobnej do powyższej często otrzymujesz zoptymalizowany do tego serwer sieciowy, który przepisuje dla niego adresy URL (prawdziwy „URL nie SEOedowany” to:) http://www.domain.tld/index.php?route=special-offers/list-all&limit=25.

Dlatego jako programista masz do czynienia z istniejącą infrastrukturą i dostosowujesz swoje „najlepsze praktyki”, chyba że jesteś administratorem systemu, wiesz dokładnie, jak dostosować konfigurację przepisywania Apache / NGinx (ta ostatnia może być nieprzyjemna!) I tak dalej na.

Tak więc interfejs API REST często byłby znacznie lepszy zgodnie ze standardami aplikacji internetowej, zarówno pod względem zgodności z nim, jak i łatwości / szybkości (a tym samym oszczędności budżetu).

Aby powrócić do powyższego praktycznego przykładu, spójny interfejs API REST byłby czymś o adresach URL takich jak:

http://www.domain.tld/api/special-offers-list?from=15&limit=25

lub (adresy URL inne niż SEO)

http://www.domain.tld/index.php?route=api/special-offers-list?from=15&limit=25

z mieszanką argumentów „utworzonych ścieżek” i argumentów „utworzonych zapytań”.

Dario Fumagalli
źródło
1

Widzę wiele interfejsów API REST, które nie radzą sobie dobrze z parametrami. Jednym z często pojawiających się przykładów jest sytuacja, w której identyfikator URI zawiera dane osobowe.

http://software.danielwatrous.com/design-principles-for-rest-apis/

Myślę, że następczym pytaniem jest, kiedy parametr w ogóle nie powinien być parametrem, ale powinien zostać przeniesiony do NAGŁÓWKA lub CIAŁA żądania.

Daniel Watrous
źródło
0

To bardzo interesujące pytanie.

Możesz użyć obu z nich, nie ma ścisłej reguły na ten temat, ale korzystanie ze zmiennych ścieżki URI ma pewne zalety:

  • Pamięć podręczna : większość internetowych usług pamięci podręcznej w Internecie nie buforuje żądania GET, gdy zawierają parametry zapytania. Robią to, ponieważ istnieje wiele systemów RPC korzystających z żądań GET do zmiany danych na serwerze (niepowodzenie !! Get musi być bezpieczną metodą)

Ale jeśli użyjesz zmiennych ścieżki, wszystkie te usługi mogą buforować twoje żądania GET.

  • Hierarchia : Zmienne ścieżki mogą reprezentować hierarchię: / Miasto / Ulica / Miejsce

Daje użytkownikowi więcej informacji na temat struktury danych.

Ale jeśli twoje dane nie mają żadnej relacji hierarchicznej, nadal możesz używać zmiennych ścieżki, używając przecinka lub średnika:

/ Miasto / długość geograficzna, szerokość geograficzna

Z reguły używaj przecinka, gdy kolejność parametrów ma znaczenie, użyj średnika, gdy kolejność nie ma znaczenia:

/ IconGenerator / czerwony; niebieski; zielony

Oprócz tych powodów istnieją przypadki, w których bardzo często używa się zmiennych ciągu zapytania:

  • Gdy potrzebujesz, aby przeglądarka automatycznie wstawiała zmienne formularza HTML do identyfikatora URI
  • Kiedy masz do czynienia z algorytmem. Na przykład silnik Google używa ciągów zapytań:

http: // www.google.com/search?q=rest

Podsumowując, nie ma żadnego silnego powodu, aby używać jednej z tych metod, ale w miarę możliwości należy używać zmiennych URI.

jfcorugedo
źródło