Podczas projektowania interfejsu RESTful semantykę typów żądań uważa się za kluczową dla projektu.
- GET - Wyświetl listę elementów do pobrania lub pobierania
- PUT - zamień kolekcję lub element
- POST - Utwórz kolekcję lub element
- USUŃ - No cóż, usuń kolekcję lub element
Nie wydaje się to jednak obejmować pojęcia „wyszukiwania”.
Na przykład przy projektowaniu pakietu usług internetowych, które obsługują witrynę Job Search, możesz mieć następujące wymagania:
- Uzyskaj indywidualną ofertę pracy
- GET do
domain/Job/{id}/
- GET do
- Utwórz ogłoszenie o pracę
- POST do
domain/Job/
- POST do
- Zaktualizuj ogłoszenie o pracę
- PUT do
domain/Job/
- PUT do
- Usuń ogłoszenie o pracę
- USUŃ do
domain/Job/
- USUŃ do
„Zdobądź wszystkie prace” jest również proste:
- GET do
domain/Jobs/
Jednak w jaki sposób „wyszukiwanie” pracy mieści się w tej strukturze?
Państwo mogłoby twierdzą, że jest to odmiana „lista kolekcji” i wdrożyć jako:
- GET do
domain/Jobs/
Jednak wyszukiwania mogą być złożone i całkowicie możliwe jest utworzenie wyszukiwania, które generuje długi ciąg GET. Oznacza to, że odwołując się tutaj do pytania SO , występują problemy z ciągami GET dłuższymi niż około 2000 znaków.
Przykładem może być wyszukiwanie fasetowe - kontynuacja przykładu „praca”.
Mogę zezwolić na wyszukiwanie w aspektach - „Technologia”, „Stanowisko”, „Dyscyplina”, a także tekstowe słowa kluczowe, wiek pracy, lokalizacja i wynagrodzenie.
Dzięki płynnemu interfejsowi użytkownika oraz dużej liczbie technologii i tytułów pracy możliwe jest, że wyszukiwanie może obejmować wiele różnych aspektów.
Ulepsz ten przykład w CV, a nie w zadaniach, przynoszą jeszcze więcej aspektów, a możesz bardzo łatwo wyobrazić sobie wyszukiwanie z wybranymi setkami aspektów lub nawet tylko 40 aspektami, z których każdy ma długość 50 znaków (np. Tytuły pracy, nazwy uniwersytetów, Nazwy pracodawców).
W takiej sytuacji pożądane może być przeniesienie PUT lub POST, aby upewnić się, że dane wyszukiwania zostaną poprawnie wysłane. Na przykład:
- POST do
domain/Jobs/
Ale semantycznie jest to instrukcja tworzenia kolekcji.
Państwo mogli również powiedzieć, musisz wyrazić to jak stworzenia wyszukiwania:
- POST do
domain/Jobs/Search/
lub (zgodnie z sugestią Burngramma poniżej)
- POST do
domain/JobSearch/
Semantycznie może się to wydawać sensowne, ale tak naprawdę nic nie tworzysz, tylko żądasz danych.
Więc semantycznie jest to GET , ale nie ma gwarancji , że GET będzie obsługiwał to, czego potrzebujesz.
Tak więc pytanie brzmi - starając się zachować jak najbardziej wierny projektowi RESTful, jednocześnie upewniając się, że trzymam się ograniczeń HTTP, jaki jest najbardziej odpowiedni projekt do wyszukiwania?
źródło
domain/Jobs?keyword={keyword}
. To działa dobrze dla mnie :) Mam nadzieję, żeSEARCH
czasownik stanie się standardem. programmers.stackexchange.com/questions/233158/…Odpowiedzi:
Nie należy zapominać, że żądania GET mają przewagę nad innymi rozwiązaniami:
1) Żądania GET można kopiować z paska adresu URL, są one przetwarzane przez wyszukiwarki, są „przyjazne”. Gdzie „przyjazny” oznacza, że zwykle żądanie GET nie powinno modyfikować niczego w Twojej aplikacji (idempotent) . Jest to standardowy przypadek wyszukiwania.
2) Wszystkie te koncepcje są bardzo ważne nie tylko z punktu widzenia użytkownika i wyszukiwarki, ale z architektonicznego punktu widzenia projektowania interfejsu API .
3) Jeśli stworzysz obejście za pomocą POST / PUT , będziesz mieć problemy, o których teraz nie myślisz. Na przykład w przypadku przeglądarki przycisk nawiguj wstecz / odśwież stronę / historię. Można to oczywiście rozwiązać, ale to będzie kolejne obejście, potem kolejne i kolejne ...
Biorąc to wszystko pod uwagę, moja rada byłaby następująca:
a) Powinieneś być w stanie zmieścić się w swoim GET przy użyciu sprytnej struktury parametrów . W skrajnym przypadku możesz nawet skorzystać z taktyki, takiej jak wyszukiwarka Google, w której ustawiłem wiele parametrów, ale jest to bardzo krótki adres URL.
b) Utwórz inny podmiot w swojej aplikacji, taki jak JobSearch . Zakładając, że masz tyle opcji, prawdopodobne jest, że będziesz musiał również przechowywać te wyszukiwania i zarządzać nimi, więc po prostu wyczyść aplikację. Możesz pracować z obiektami JobSearch jako całością, co oznacza, że możesz je przetestować / używać łatwiej .
Osobiście starałbym się walczyć wszystkimi pazurami, aby to zrobić za pomocą a), a gdy cała nadzieja została utracona, czołgałam się ze łzami w oczach do opcji b) .
źródło
domain/Jobs/Search/
, być może zedomain/JobsSearch/
zamiast, albo miałeś na myśli coś innego? Możesz wyjaśnić?TL; DR: GET do filtrowania, POST do wyszukiwania
Dokonuję rozróżnienia między filtrowaniem wyników z listy kolekcji a złożonym wyszukiwaniem. Test lakmusowy, którego używam, jest w zasadzie, jeśli potrzebuję więcej niż filtrowania (dodatniego, ujemnego lub zakresu), uważam to za bardziej złożone wyszukiwanie wymagające POST.
Jest to zwykle wzmacniane, gdy myśli się o tym, co zostanie zwrócone. Zwykle używam GET tylko wtedy, gdy zasób ma przeważnie pełny cykl życia (PUT, DELETE, GET, collection GET) . Zazwyczaj w kolekcji GET zwracam listę identyfikatorów URI, które są zasobami REST, które tworzą tę kolekcję. W złożonym zapytaniu mogę pobierać dane z wielu zasobów, aby skonstruować odpowiedź (pomyślmy o połączeniu SQL), więc nie będę odsyłał identyfikatorów URI, ale rzeczywiste dane. Problem polega na tym, że dane nie będą reprezentowane w zasobie, więc zawsze będę musiał zwrócić dane. Wydaje mi się to oczywistym przypadkiem konieczności POST.
-
Minęło trochę czasu, a mój oryginalny post był nieco niechlujny, więc pomyślałem, że zaktualizuję.
GET to intuicyjny wybór do zwracania większości rodzajów danych, kolekcji zasobów REST, danych strukturalnych zasobu, a nawet pojedynczych ładunków (obrazów, dokumentów itp.).
POST to metoda catchall na wszystko, co nie pasuje do GET, PUT, DELETE itp.
W tym momencie myślę, że proste wyszukiwania, filtrowanie intuicyjnie mają sens przez GET. Wyszukiwanie złożone zależy od osobistych preferencji, szczególnie jeśli dodajesz funkcje agregacji, korelacje krzyżowe (sprzężenia), przekształcanie formatów itp. Argumentowałbym, że parametry GET nie powinny być zbyt długie i jest to duże zapytanie (jednak jest ono ustrukturyzowane ) często ma większy sens jako treść żądania POST.
Rozważam także aspekt korzystania z interfejsu API. Zasadniczo chcę, aby większość metod była jak najłatwiejsza w obsłudze i intuicyjna. Będę wypychać połączenia, które są bardziej elastyczne (a zatem bardziej złożone) do POST i na inny identyfikator URI zasobu, szczególnie jeśli jest to niezgodne z zachowaniem innych zasobów REST w tym samym interfejsie API.
Tak czy inaczej, spójność jest prawdopodobnie ważniejsza niż to, czy przeprowadzasz wyszukiwanie w GET, czy POST.
Mam nadzieję że to pomoże.
źródło
GET /class?queryParams
. Z punktu widzenia użytkownika „klasa” zawsze była rzeczą i nie trzeba było wykonywać żadnych dziwnych połączeń SQL.W REST definicja zasobów jest bardzo szeroka. Naprawdę jednak chcesz połączyć pewne dane.
Na przykład główny identyfikator URI Google wskazuje na zbiór zasobów „linków do każdej witryny w Internecie”. Parametry zapytania zawężają to do witryn, które chcesz zobaczyć.
(URI = uniwersalny identyfikator zasobu, którego URL = uniwersalny lokalizator zasobów, gdzie znany „http: //” jest domyślnym formatem identyfikatora URI. Zatem URL jest lokalizatorem, ale w REST dobrze jest uogólnić to na identyfikator zasobu Ludzie używają ich jednak zamiennie).
A następnie użyj POST, czyli czasownika dołączającego lub procesowego, aby dodać nowe elementy do tej kolekcji:
Zauważ, że
job
w każdym przypadku jest to ta sama struktura dla obiektu. Klient może UZYSKAĆ kolekcję zadań, używając parametrów zapytania w celu zawężenia wyszukiwania, a następnie użyć tego samego formatu dla jednego z elementów, aby opublikować nowe zadanie. Lub może zabrać jeden z tych elementów i PUT do swojego identyfikatora URI, aby go zaktualizować.W przypadku naprawdę długich lub skomplikowanych ciągów zapytań konwencja pozwala zamiast tego wysyłać je jako żądania POST. Sparuj parametry zapytania jako pary nazwa / wartość lub zagnieżdżone obiekty w strukturze JSON lub XML i wyślij je w treści żądania. Na przykład, jeśli zapytanie zawiera zagnieżdżone dane zamiast kilku par nazwa / wartość. Specyfikacja HTTP dla POST opisuje ją jako czasownik dołączający lub procesowy. (Jeśli chcesz popłynąć pancernikiem przez lukę w REST, użyj POST.)
Użyłbym tego jednak jako planu awaryjnego.
To, co tracisz, gdy to robisz, to: a) GET jest nullipotentny - to znaczy nic nie zmienia - POST nie. Jeśli więc połączenie się nie powiedzie, oprogramowanie pośrednie nie będzie automatycznie ponawiać próby ani buforować wyników oraz 2) z parametrami wyszukiwania w treści, nie można już wycinać i wklejać identyfikatora URI. Oznacza to, że identyfikator URI nie jest konkretnym identyfikatorem poszukiwanego wyszukiwania.
Aby odróżnić „Utwórz” od „Szukaj”. Istnieje kilka opcji zgodnych z praktyką REST:
Możesz to zrobić w URI, dodając coś do nazwy kolekcji, np. Poszukiwanie pracy zamiast zadań. Oznacza to, że traktujesz kolekcję wyszukiwania jako osobny zasób.
Ponieważ semantyka POST jest procesem dołączania LUB, można wyszukiwać ciała wyszukiwania z ładunkiem. Jak {job: ...} vs. {search: ...}. Zadaniem logiki POST jest odpowiednie opublikowanie lub przetworzenie.
To właściwie preferencja dotycząca projektowania / implementacji. Nie sądzę, że istnieje jasna konwencja.
Tak więc, jak już zaplanowałeś, pomysł polega na zdefiniowaniu zasobu kolekcji
jobs
Wyszukaj za pomocą parametrów zapytania GET +, aby zawęzić wyszukiwanie. Długie lub ustrukturyzowane zapytania o dane trafiają do treści POST (być może do oddzielnej kolekcji wyszukiwania). Utwórz za pomocą POST, aby dołączyć do kolekcji. I zaktualizuj za pomocą PUT do konkretnego identyfikatora URI.
(FWIW konwencja stylu z identyfikatorami URI polega na użyciu wszystkich małych liter ze słowami oddzielonymi łącznikami. Ale to nie znaczy, że musisz to zrobić w ten sposób.)
(Powinienem również powiedzieć, że z twojego pytania jasno wynika, że jesteś daleko na tej drodze. Przeliterowałem pewne rzeczy, aby je uporządkować, ale twoje pytanie już dotyczyło większości problemów semantycznych w tym odpowiedź. Po prostu łączyłem to z konwencją i praktyką.)
źródło
route
tak naprawdę nie jest w stanie obsłużyć wyboru przetwarzania. Muszę rzucić na to okiem ...Zazwyczaj używam zapytań OData, działają one jako wywołanie GET, ale pozwalają ci ograniczyć zwracane właściwości i filtrować je.
Używasz tokenów, takich jak
$select=
i,$filter=
więc otrzymujesz identyfikator URI, który wygląda mniej więcej tak:Można również zrobić przy użyciu stronicowania
$skip
i$top
i zamawiania.Aby uzyskać więcej informacji, sprawdź OData.org . Nie określiłeś, jakiego języka używasz, ale jeśli jest to ASP.NET, platforma WebApi obsługuje zapytania OData - dla innych (PHP itp.) Prawdopodobnie są biblioteki, których możesz użyć do przetłumaczenia ich na zapytania do bazy danych.
źródło
JobsNearMeAddedInTheLast7Days
lub cokolwiek innego, aby obudować zapytanie, które jest zbyt długie / złożone dla OData, a następnie ujawniam je tylko za pomocą wywołań GET .Jednym podejściem do rozważenia jest traktowanie zestawu możliwych zapytań jako zasobu kolekcji, np
/jobs/filters
.POST
wnioski do tego zasobu, z parametrów kwerendy w organizmie, albo będzie utworzyć nowy zasób lub wyznaczyć istniejący odpowiednik filtra i powrotu URL zawierający jego ID:/jobs/filters/12345
.Identyfikator może być następnie wykorzystane w żądaniu GET dla zadań:
/jobs?filter=12345
. KolejneGET
żądania dotyczące zasobu filtru zwrócą definicję filtra.Zaletą tego podejścia jest to, że uwalnia cię od formatu parametru zapytania do definicji filtra, potencjalnie zapewniając więcej mocy do definiowania złożonych filtrów. Warunki OR są jednym z przykładów, które mogę wymyślić, które są trudne do spełnienia za pomocą ciągów zapytań.
Wadą tego podejścia jest utrata czytelności adresu URL (chociaż można to złagodzić, pobierając definicję przez
GET
żądanie zasobu filtru). Z tego powodu możesz również chcieć obsługiwać ten sam lub podzbiór parametrów zapytania w/jobs
zasobie, tak jak w przypadku zasobu filtrującego. Można to wykorzystać do krótszych zapytań. Jeśli ta funkcja jest dostępna, aby zachować pamięć podręczną między dwoma typami filtrowania, podczas korzystania z parametrów zapytania w/jobs
zasobie implementacja powinna wewnętrznie utworzyć / ponownie użyć zasobu filtru i zwrócić wartość302
lub303
status wskazujący adres URL w postaci/jobs?filter=12345
.źródło
GET /jobs/37
i otrzymać wynik, a następnie ktoś usunie zasób, a 2 sekundy później to samo żądanie zwróci 404. Podobnie, jeśli zostanieszPOST /searches
przekierowany do wyniku wyszukiwania (wyszukiwanie zostanie utworzone, a otrzymasz 201 z Lokalizacja nagłówka zasobu), 2 sekundy później wynik może zostać usunięty z pamięci i trzeba go zregenerować. Nie ma potrzeby przechowywania długoterminowego.To stara odpowiedź, ale nadal mogę trochę przyczynić się do dyskusji. Bardzo często obserwowałem nieporozumienie dotyczące REST, RESTful i architektury. RESTful nigdy nie wspomina nic o wyszukiwaniu NIE, nie ma nic w RESTful o architekturze, to zestaw zasad lub kryteriów projektowych.
Aby lepiej opisać wyszukiwanie, musimy porozmawiać w szczególności o architekturze, a ta, która lepiej pasuje, to architektura zorientowana na zasoby (ROA).
W RESTful istnieją zasady projektowania, idempotent nie oznacza, że wynik nie może się zmienić, jak czytam w niektórych odpowiedziach, oznacza to, że wynik niezależnego żądania nie zależy od tego, ile razy zostanie wykonany. Może się zmieniać, wyobraźmy sobie, że ciągle aktualizuję bazę danych, zasilając ją niektórymi danymi, które są obsługiwane przez RESTful API, wykonanie tego samego GET może zmienić wynik, ale nie zależy to od tego, ile razy zostało wykonane. Jeśli jestem w stanie zamrozić świat, oznacza to, że nie ma stanu, transformacji ani niczego w usłudze, gdy żądam zasobu, który prowadzi do innego wyniku.
W architekturze zorientowanej na zasoby (nazwijmy to teraz ROA dla zwięzłości) skupiamy się na zasobie, który może być wieloma rzeczami:
To, co czyni go wyjątkowym pod względem zasobów, to adresowalność, co oznacza, że ma tylko jeden identyfikator URI
W ten sposób wyszukiwanie idealnie pasuje do RESTful, biorąc pod uwagę ROA . Musimy użyć GET, ponieważ zakładam, że twoje wyszukiwanie jest normalnym wyszukiwaniem i niczego nie zmienia, więc jest idempotentne (nawet jeśli zwraca różne rzeczy w zależności od dodanych nowych elementów). Jest w tym zamieszanie, ponieważ mogę trzymać się RESTful, a nie ROA, oznacza to, że mogę podążać za wzorem, który tworzy wyszukiwanie i zwracać różne rzeczy o tych samych parametrach, ponieważ nie używam zasady adresowalności ROA. W jaki sposób? Cóż, jeśli wyślesz filtry wyszukiwania w treści lub nagłówku, zasób nie będzie ADRESOWALNY.
Możesz znaleźć zasady dokładnie tego, co dokładnie i URI w oryginalnym dokumencie W3:
https://www.w3.org/DesignIssues/Axioms
Każdy adres URL w tej architekturze musi być opisowy. Jest to konieczne, jeśli postępujesz zgodnie z zasadami, aby zająć się wszystkim w URI, oznacza to, że możesz użyć / (ukośnik), aby oddzielić wszystko, czego potrzebujesz lub parametry zapytania. Wiemy, że istnieją ograniczenia, ale jest to wzorzec architektury.
Zgodnie z wzorcem ROA w RESTful wyszukiwanie nie jest większe niż jakikolwiek inny zasób, jedyną różnicą jest to, że zasoby pochodzą z obliczeń zamiast bezpośredniego związku z samym obiektem. W oparciu o zasadę mógłbym zająć się i uzyskać prostą usługę obliczeń arytmetycznych w oparciu o następujący wzorzec:
http://myapi.com/sum/1/2
Tam, gdzie sumę 1 i 2 można modyfikować, ale wynik obliczeń jest unikalny i można go adresować, za każdym razem, gdy dzwonię z tymi samymi parametrami, uzyskuję to samo i nic się nie zmienia w serwisie. Resouce / sum / 1/2 i / subtract / 5/4 doskonale trzymają się zasad.
źródło
GET jest w porządku, jeśli masz kolekcję statyczną, która zawsze zwraca te same wyniki (reprezentację) dla jednego identyfikatora URI. Oznacza to również, że dane generujące te reprezentacje nigdy nie są zmieniane. Źródłem jest baza danych tylko do odczytu.
Otrzymywanie przez GET różnych wyników dla tego samego identyfikatora URI narusza idempotencja / bezpieczeństwo i zasadę CoolURI, w związku z czym nie jest w stanie RESTful . Czasami idempotentne czasowniki mogą być zapisywane w bazie danych, ale nigdy nie mogą wpływać na reprezentację.
Wspólne wyszukiwanie rozpoczyna się od żądania POST, które zwraca odniesienie do wyniku. Generuje wynik (jest nowy i można go pobrać z kolejnym GET). Ten wynik może być hierarchiczny (dalsze odwołania do identyfikatorów URI, które można uzyskać) i może ponownie wykorzystywać elementy wcześniejszych wyszukiwań, jeśli ma to sens dla aplikacji.
Nawiasem mówiąc, wiem, że ludzie robią to inaczej. Nie musisz mi wyjaśniać, jak wygodne może być naruszenie REST.
źródło