Kiedy używać parametrów ścieżki, a kiedy parametrów zapytań w RESTful API?

141

Chcę, aby mój interfejs API RESTful był bardzo przewidywalny. Jaka jest najlepsza praktyka przy podejmowaniu decyzji, kiedy dokonać segmentacji danych przy użyciu identyfikatora URI, a nie przy użyciu parametrów zapytania.

Wydaje mi się sensowne, że parametry systemowe obsługujące paginację, sortowanie i grupowanie znajdują się po znaku „?” A co z polami takimi jak „stan” i „region” lub innymi atrybutami, które dzielą Twoją kolekcję na segmenty? Jeśli mają to być również parametry zapytania, jaka jest praktyczna zasada dotycząca tego, kiedy należy używać parametrów ścieżki?

cosbor11
źródło
1
odpowiedź na podobne pytanie znajduje się tutaj ... stackoverflow.com/questions/3198492/…
Lalit Mehra

Odpowiedzi:

239

Najlepszą praktyką w projektowaniu interfejsu API zgodnego z REST jest to, że parametry ścieżki są używane do identyfikowania określonego zasobu lub zasobów, a parametry zapytania są używane do sortowania / filtrowania tych zasobów.

Oto przykład. Załóżmy, że implementujesz punkty końcowe interfejsu API RESTful dla jednostki o nazwie Car. Skonstruowałbyś swoje punkty końcowe w następujący sposób:

GET /cars
GET /cars/:id
POST /cars
PUT /cars/:id
DELETE/cars/:id

W ten sposób parametry ścieżki są używane tylko podczas określania zasobu do pobrania, ale w żaden sposób nie sortuje / filtruje zasobów.

Załóżmy teraz, że chcesz dodać możliwość filtrowania samochodów według koloru w żądaniach GET. Ponieważ kolor nie jest zasobem (jest właściwością zasobu), można dodać parametr zapytania, który to robi. Dodałbyś ten parametr zapytania do żądania GET w/cars następujący sposób:

DOSTAĆ /cars?color=blue

Ten punkt końcowy zostałby zaimplementowany, aby zwracane były tylko niebieskie samochody.

Jeśli chodzi o składnię, nazwy adresów URL powinny być pisane małymi literami. Jeśli masz nazwę jednostki, która zazwyczaj składa się z dwóch słów w języku angielskim, do oddzielenia słów należy użyć łącznika, a nie wielkości wielbłąda.

Dawny. /two-words

Mikrofon
źródło
3
Dziękuję za odpowiedź Mike. To jest wyraźna i prosta metodologia; warte głosu ode mnie. Mimo to często deweloperzy wybierają podejście `` samochody / niebieski '', zastanawiam się, jakie jest ich uzasadnienie ... może decydują się na wprowadzenie parametrów ścieżki dla pól, które są obowiązkowe, a może robią to, aby wskazać, że baza danych jest partycjonowana przez ten fragment.
cosbor11
1
Nie jestem pewien, jakie jest ich uzasadnienie. Szczerze, nie zgadzam się z tym. Myślę, że najbardziej sensowne jest przestrzeganie konwencji i prostota. W ten sposób umożliwiasz konsumentom twojego API lepsze zrozumienie, co dokładnie muszą zrobić, aby uzyskać dostęp do jego funkcjonalności.
Mike,
3
a co z / cars? id = 1 & color = blue zamiast cars / 1 /? color = blue. w zasadzie filtrujesz zasoby samochodów w każdym scenariuszu
mko
1
Źle, ponieważ samochód o identyfikatorze 1 istnieje tylko jeden, ale samochodów w kolorze niebieskim może być wiele. Istnieje różnica między tożsamością a filtrem
Paweł
1
Moja hipoteza, dlaczego używanie parametrów ścieżki jest tak powszechne, jest taka, że ​​wielu programistów nauczyło się z frameworków zaprojektowanych przez ludzi, którzy nie mieli dobrego pojęcia o zasadach REST (w szczególności Ruby on Rails).
Chris Broski,
58

Podstawowy sposób myślenia na ten temat jest następujący:

URI to identyfikator zasobu, który jednoznacznie identyfikuje określone wystąpienie TYPU zasobu. Jak wszystko inne w życiu, każdy obiekt (który jest instancją pewnego typu) ma zestaw atrybutów, które są albo niezmienne w czasie, albo czasowe.

W powyższym przykładzie samochód jest bardzo namacalnym przedmiotem, który ma takie atrybuty, jak marka, model i VIN - które nigdy się nie zmieniają, a kolor, zawieszenie itp. Mogą się zmieniać w czasie. Więc jeśli zakodujemy URI z atrybutami, które mogą się zmieniać w czasie (czasowo), możemy skończyć z wieloma identyfikatorami URI dla tego samego obiektu:

GET /cars/honda/civic/coupe/{vin}/{color=red}

A po latach, jeśli kolor tego samego samochodu zmieni się na czarny:

GET /cars/honda/civic/coupe/{vin}/{color=black}

Zwróć uwagę, że sama instancja samochodu (obiekt) nie uległa zmianie - zmienił się tylko kolor. Posiadanie wielu identyfikatorów URI wskazujących na tę samą instancję obiektu zmusi Cię do utworzenia wielu programów obsługi URI - nie jest to efektywny projekt i oczywiście nie jest intuicyjny.

Dlatego identyfikator URI powinien składać się tylko z części, które nigdy się nie zmienią i będą nadal jednoznacznie identyfikować ten zasób przez cały okres jego istnienia. Wszystko, co może się zmienić, powinno być zarezerwowane dla parametrów zapytania, takich jak:

GET /cars/honda/civic/coupe/{vin}?color={black}

Konkluzja - pomyśl o polimorfizmie.

Kingz
źródło
2
Ciekawy paradygmat… Czy jest to powszechnie stosowany wzór projektowy? Czy możesz podać interfejsy API, które używają tego w dokumentacji, lub odniesienia, które opisują tę strategię?
cosbor11
1
Podoba mi się, jak podkreśliłeś „TYPE”, pisząc „URI to identyfikator zasobu, który jednoznacznie identyfikuje określoną instancję zasobu TYPE”. Myślę, że to ważna różnica.
jrahhali
15

W REST API nie powinieneś zbytnio przejmować się przewidywalnymi identyfikatorami URI. Już sama sugestia dotycząca przewidywalności URI odnosi się do niezrozumienia architektury RESTful. Zakłada, że ​​klient powinien sam konstruować identyfikatory URI, czego naprawdę nie powinien.

Zakładam jednak, że nie tworzysz prawdziwego REST API, ale API „inspirowane REST” (takie jak Google Drive). W takich przypadkach ogólną zasadą jest „parametry ścieżki = identyfikacja zasobu” i „parametry zapytania = sortowanie zasobów”. Pojawia się więc pytanie, czy możesz jednoznacznie zidentyfikować swój zasób BEZ statusu / regionu? Jeśli tak, to być może jest to parametr zapytania. Jeśli nie, to jest to parametr ścieżki.

HTH.

Oliver McPhee
źródło
11
Nie zgadzam się, dobre API powinno być przewidywalne; RESTful lub inaczej.
cosbor11
3
Chyba tak. Powinien istnieć rym i powód, w jaki sposób tworzony jest identyfikator URI, zamiast arbitralnego nazywania punktów końcowych. Kiedy można intuicyjnie napisać klienta API bez ciągłego odwoływania się do dokumentacji, moim zdaniem napisałeś dobre API.
cosbor11
2
„Kiedy można intuicyjnie napisać klienta API bez ciągłego odwoływania się do dokumentacji”. Myślę, że właśnie w tym miejscu różni się nasze rozumienie REST ... klient API nie powinien nigdy potrzebować „budowania” adresu URL. Powinni wybrać go z odpowiedzi na poprzednie wywołanie API. Jeśli weźmiesz stronę internetową jako analogię ... Wchodzisz na facebook.com, a następnie wybierasz link do strony wydarzeń. Nie obchodzi cię, czy adres URL wydarzeń na Facebooku jest „przewidywalny”, ponieważ go nie wpisujesz. Dostajesz się tam za pośrednictwem hipermediów. To samo dotyczy interfejsu API REST. Tak więc, spraw, aby identyfikatory URI miały znaczenie dla Ciebie (serwera), ale nie dla klienta
Oliver McPhee
2
Uwaga dodana. Nie oznacza to, że identyfikatory URI nie powinny podążać za łatwym do zrozumienia wzorcem, oznacza to po prostu, że nie jest to ograniczenie RESTful API. Największym problemem w tym obszarze jest założenie, że klient powinien samodzielnie budować adresy URL. Nie powinny, ponieważ tworzy to połączenie między klientem a serwerem, które nie powinno istnieć. (np. - serwer nie może wtedy zmienić adresu URL bez zerwania wszystkich aplikacji klienckich). W REST API serwer może je zmieniać według własnego uznania.
Oliver McPhee
3
+1 za użycie następujących słów: "'parametry ścieżki = identyfikacja zasobu' i 'parametry zapytania = sortowanie zasobów'". To naprawdę wyjaśniło mi to.
Doug
3

Kiedyś zaprojektowałem API, którym był główny zasób people. Zwykle użytkownicy prosili o filtrowanie, peoplewięc aby uniemożliwić użytkownikom wywoływanie czegoś takiego za /people?settlement=urbankażdym razem, zaimplementowałem, /people/urbanco później umożliwiło mi łatwe dodawanie /people/rural. Pozwala to również na dostęp do pełnej /peoplelisty, jeśli będzie to przydatne później. Krótko mówiąc, moim rozumowaniem było dodanie ścieżki do wspólnych podzbiorów

Od tutaj :

Aliasy dla typowych zapytań

Aby uczynić interfejs API przyjemniejszym dla przeciętnego konsumenta, rozważ zestawienie zestawów warunków w łatwo dostępne ścieżki RESTful. Na przykład, ostatnio zamknięte zapytanie o bilety powyżej może być spakowane jakoGET /tickets/recently_closed

Mario Gil
źródło
1

Ogólnie rzecz biorąc, zwykle używam parametrów ścieżki, gdy w zasobie istnieje oczywista „hierarchia”, na przykład:

/region/state/42

Jeśli ten pojedynczy zasób ma status, można:

/region/state/42/status

Jeśli jednak „region” nie jest tak naprawdę częścią udostępnianego zasobu, prawdopodobnie należy do jednego z parametrów zapytania - podobnie jak w przypadku paginacji (jak wspomniałeś).

morsor
źródło
0

Segmentacja jest bardziej hierarchiczna i „ładna”, ale może być ograniczająca.

Na przykład, jeśli masz adres URL z trzema segmentami, z których każdy przekazuje inne parametry wyszukiwania samochodu według marki, modelu i koloru:

www.example.com/search/honda/civic/blue

Jest to bardzo ładny adres URL i łatwiejszy do zapamiętania przez użytkownika końcowego, ale teraz utknąłeś w tej strukturze. Powiedzmy, że chcesz, aby podczas wyszukiwania użytkownik mógł znaleźć WSZYSTKIE niebieskie samochody lub WSZYSTKIE samochody Honda Civics? Parametr zapytania rozwiązuje ten problem, ponieważ podaje parę klucz-wartość. Więc możesz przejść:

www.example.com/search?color=blue
www.example.com/search?make=civic

Teraz masz sposób na odwołanie się do wartości za pomocą jej klucza - „kolor” lub „marka” w kodzie zapytania.

Możesz obejść ten problem, używając większej liczby segmentów, aby stworzyć rodzaj struktury kluczowej wartości, takiej jak:

www.example.com/search/make/honda/model/civic/color/blue

Mam nadzieję, że to ma sens ...

webmaster_sean
źródło
-2

Przykładowy adres URL: /rest/{keyword}

Ten adres URL jest przykładem parametrów ścieżki. Możemy uzyskać te dane URL za pomocą @PathParam.

Przykładowy adres URL: /rest?keyword=java&limit=10

Ten adres URL jest przykładem parametrów zapytania. Możemy uzyskać te dane URL za pomocą @Queryparam.

Sujithrao
źródło