Zaprojektuj interfejs API zapytań RESTful z długą listą parametrów zapytania [zamknięte]

153

Muszę zaprojektować API zapytań RESTful, które zwraca zestaw obiektów na podstawie kilku filtrów. Zwykłą metodą HTTP jest to GET. Jedynym problemem jest to, że może mieć co najmniej kilkanaście filtrów, a jeśli przekażemy je wszystkie jako parametry zapytania, adres URL może być dość długi (na tyle długi, że może zostać zablokowany przez jakąś zaporę).

Zmniejszenie liczby parametrów nie wchodzi w grę.

Jedną z alternatyw, o których mógłbym pomyśleć, jest użycie metody POST w identyfikatorze URI i wysłanie filtrów jako części treści POST. Czy to przeciwko byciu RESTfull (Wykonywanie wywołania POST w celu zapytania o dane).

Czy ktoś ma lepsze sugestie dotyczące projektu?

misjaE46
źródło
2
Czy używać krótkich (1-znakowych itp.) Nazw parametrów?
Madbreaks
2
Może nie jest to naprawdę RESTful, ale myślę, że musisz być praktyczny, jeśli chodzi o GET i POST. Jeśli masz tyle zmiennych do wysłania i nie możesz ich zredukować, POSTAŁEM je. Nie lubię przepychania adresu URL, ale to tylko ja.
Doug Dawson
Dzięki. Chociaż to pytanie jest zamknięte, to DOKŁADNIE jest to pytanie, na które potrzebowałem odpowiedzi. Cieszę się, że zapytałeś.
Casey Crookston

Odpowiedzi:

142

Pamiętaj, że w przypadku REST API wszystko zależy od Twojego punktu widzenia.

Dwie kluczowe koncepcje interfejsu API REST to punkty końcowe i zasoby (jednostki). Mówiąc luźno, punkt końcowy zwraca zasoby za pośrednictwem GET lub akceptuje zasoby za pośrednictwem POST i PUT i tak dalej (lub kombinacja powyższych).

Przyjmuje się, że w przypadku POST wysyłane dane mogą, ale nie muszą, skutkować utworzeniem nowego zasobu i skojarzonego z nim punktu (-ów) końcowego (-ych), który najprawdopodobniej nie będzie „żył” pod opublikowanym adresem URL. Innymi słowy, kiedy POST wysyłasz dane gdzieś do obsługi. Punkt końcowy POST nie znajduje się tam, gdzie normalnie można znaleźć zasób.

Cytowanie z RFC 2616 (z pominięciem nieistotnych części i podświetleniem odpowiednich części):

9.5 POST

Metoda POST służy do żądania, aby serwer pochodzenia zaakceptował jednostkę zawartą w żądaniu jako nowego podrzędnego zasobu określonego przez identyfikator żądania w wierszu żądania. POST został zaprojektowany, aby umożliwić jednolitą metodę obejmującą następujące funkcje:

  • ...
  • Udostępnienie bloku danych, np. Wynikającego z przesłania formularza, do procesu obsługi danych;
  • ...

...

Akcja wykonana przez metodę POST może nie skutkować zasobem, który można zidentyfikować za pomocą identyfikatora URI . W takim przypadku odpowiedni stan odpowiedzi to 200 (OK) lub 204 (Brak treści), w zależności od tego, czy odpowiedź zawiera jednostkę opisującą wynik .

Jeśli zasób został utworzony na serwerze pochodzenia, odpowiedź POWINNA być 201 (utworzona) ...

Przyzwyczailiśmy się do punktów końcowych i zasobów reprezentujących „rzeczy” lub „dane”, czy to użytkownika, wiadomość, czy książkę - niezależnie od tego, na czym polega problem. Jednak punkt końcowy może również ujawniać inny zasób - na przykład wyniki wyszukiwania.

Rozważmy następujący przykład:

GET    /books?author=AUTHOR
POST   /books
PUT    /books/ID
DELETE /books/ID

To jest typowy REST CRUD. A co by było, gdybyśmy dodali:

POST /books/search

    {
        "keywords": "...",
        "yearRange": {"from": 1945, "to": 2003},
        "genre": "..."
    }

W tym punkcie końcowym nie ma nic nie-REST. Przyjmuje dane (podmiot) w postaci treści żądania. Te dane to kryteria wyszukiwania - DTO jak każde inne. Ten punkt końcowy tworzy zasób (jednostkę) w odpowiedzi na żądanie: Wyniki wyszukiwania . Zasób wyników wyszukiwania jest zasobem tymczasowym, udostępnianym klientowi natychmiast, bez przekierowania i bez ujawniania się z innego kanonicznego adresu URL.

Nadal jest REST, z wyjątkiem tego, że encje nie są książkami - encja żądania to kryteria wyszukiwania książek, a encja odpowiedzi to wyniki wyszukiwania książek.

Amir Abiri
źródło
Czy możesz zasugerować konwencje nazewnictwa klas dla DTO?
Kwadz
Osobiście poszedłbym z BooksSearchCriteriaDTOi BooksSearchResultsDTO.
Amir Abiri
jaki byłby najlepszy kod odpowiedzi HTTP dla tego przypadku POST / books / search? 201 nadal obowiązuje?
L. Holanda
9
201 jest odwrotnie - oznacza to, że zasób został utworzony. Zasób, który powinien mieć gdzieś swój unikalny identyfikator URI. 201 jest odpowiedni, gdy POSTjest używany do części C CRUD. Wybrałbym zwykłe stare 200, opcjonalnie może 204 dla pustych wyników wyszukiwania.
Amir Abiri
@AmirAbiri bardzo dziękuję.
mohamed-mhiri
84

Wiele osób zaakceptowało praktykę, że GET ze zbyt długim lub zbyt złożonym ciągiem zapytania (np. Ciągi zapytań nie obsługują łatwo zagnieżdżonych danych) można zamiast tego wysłać jako POST, ze złożonymi / długimi danymi reprezentowanymi w treści wniosku.

Wyszukaj specyfikację POST w specyfikacji HTTP. Jest niesamowicie szeroki. (Jeśli chcesz przepłynąć pancernikiem przez lukę w REST ... użyj POST.)

Tracisz niektóre korzyści płynące z semantyki GET ... takie jak automatyczne ponawianie próby, ponieważ GET jest idempotentny, ale jeśli możesz z tym żyć, łatwiej będzie zaakceptować przetwarzanie naprawdę długich lub skomplikowanych zapytań za pomocą POST.

(lol długa dygresja ... Niedawno odkryłem, że zgodnie ze specyfikacją HTTP GET może zawierać treść dokumentu. Jest jedna sekcja, która mówi parafrazując: „Każde żądanie może mieć treść dokumentu oprócz tych wymienionych w tej sekcji” ... a sekcja, do której się odnosi, nie zawiera żadnego. Przeszukałem i znalazłem wątek, w którym autorzy HTTP rozmawiali o tym, i było to zamierzone, aby routery i inne nie musiały rozróżniać różnych wiadomości. ćwiczyć wiele elementów infrastruktury, co powoduje upuszczenie treści GET. Możesz więc GET z filtrami reprezentowanymi w treści, np. POST, ale będziesz rzucał kostką).

Obrabować
źródło
11
Zobacz także to pytanie, aby uzyskać więcej dyskusji na temat HTTP GET z treścią.
RickyA,
6

W skrócie: utwórz POST, ale zastąp metodę HTTP za pomocą nagłówka X-HTTP-Method-Override .

Prawdziwa prośba

POST / książki

Jednostka podmiotu

{"title": "Ipsum", "year": 2017}

Nagłówki

X-HTTP-Method-Override: GET

Po stronie serwera sprawdź, czy istnieje nagłówek X-HTTP-Method-Override, a następnie przyjmij jego wartość jako metodę tworzenia trasy do końcowego punktu końcowego w zapleczu. Weź również treść jednostki jako ciąg zapytania. Z punktu widzenia zaplecza żądanie stało się po prostu prostym GET.

W ten sposób projekt pozostaje w harmonii z zasadami REST.

Edycja: Wiem, że to rozwiązanie było pierwotnie przeznaczone do rozwiązania problemu czasownika PATCH w niektórych przeglądarkach i serwerach, ale działa również dla mnie z czasownikiem GET w przypadku bardzo długiego adresu URL, który jest problemem opisanym w pytaniu.

Delmo
źródło
2
IETF Nieaktualne X- ustalonej uprzednio nagłówki HTTP: tools.ietf.org/html/rfc6648
Jannis
@jannis Łącza RFC, z którymi się łączysz, pozostają 1,4. nie zawiera zaleceń dotyczących istniejącego X-usunięcia i 1.5. nie zastępuje istniejących specyfikacji. ... X-czy IMO pozostanie tutaj.
Jan Molnar,
-3

Jeśli programujesz w Javie i JAX-RS, polecam używanie @QueryParam z @GET

Miałem to samo pytanie, kiedy musiałem przejrzeć listę.

Zobacz przykład:

import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

@Path("/poc")
public class UserService {

    @GET
    @Path("/test/")
    @Produces(MediaType.APPLICATION_JSON)
    public Response test(@QueryParam("code") final List<Integer> code) {
                Integer int0 = codigo.get(0);
                Integer int1 = codigo.get(1);

        return Response.ok(new JSONObject().put("int01", int0)).build();
    }
}

Wzorzec URI: „poc / test? Code = 1 & code = 2 & code = 3

@QueryParam automatycznie skonwertuje parametr zapytania „orderBy = age & orderBy = name” na java.util.List.

acacio.martins
źródło
Byłoby lepiej, gdybyś wyjaśnił swój przykład. W jakim języku programowania jest napisane?
Aleks Andreev
Cześć @AleksAndreev. Dziękuję za twoją opinię. Zrobiło się lepiej? tks
acacio.martins
To pytanie dotyczy projektu usługi RESTful, a nie implementacji. Ta odpowiedź nie odpowiada na pytanie.
Heretic Monkey
@ user1331413 IMHO tak, teraz jest lepiej. Dziękuję za twój wysiłek. Jednak, jak powiedział Mike McCaughan, pytanie dotyczy raczej koncepcji REST niż implementacji
Aleks Andreev