Obecnie projektuję i wdrażam API RESTful w PHP. Jednak nie udało mi się wdrożyć mojego początkowego projektu.
GET /users # list of users
GET /user/1 # get user with id 1
POST /user # create new user
PUT /user/1 # modify user with id 1
DELETE /user/1 # delete user with id 1
Jak dotąd dość standardowy, prawda?
Mój problem dotyczy pierwszego GET /users
. Zastanawiałem się nad przesłaniem parametrów w treści żądania w celu przefiltrowania listy. Jest tak, ponieważ chcę móc określić złożone filtry bez uzyskiwania bardzo długiego adresu URL, na przykład:
GET /users?parameter1=value1¶meter2=value2¶meter3=value3¶meter4=value4
Zamiast tego chciałem mieć coś takiego:
GET /users
# Request body:
{
"parameter1": "value1",
"parameter2": "value2",
"parameter3": "value3",
"parameter4": "value4"
}
który jest znacznie bardziej czytelny i daje ogromne możliwości ustawienia złożonych filtrów.
W każdym razie file_get_contents('php://input')
nie zwrócił treści GET
żądania. Próbowałem też http_get_request_body()
, ale hostowanie współdzielone, którego używam, nie ma pecl_http
. Nie jestem pewien, czy i tak by to pomogło.
Znalazłem to pytanie i zdałem sobie sprawę, że GET prawdopodobnie nie powinien mieć treści żądania. To było trochę nieprzekonujące, ale odradzali to.
Więc teraz nie jestem pewien, co robić. Jak zaprojektować funkcję wyszukiwania / filtrowania RESTful?
Myślę, że mógłbym użyć POST
, ale to nie wydaje się bardzo ODPOCZNE.
Odpowiedzi:
Najlepszym sposobem na wdrożenie wyszukiwania RESTful jest uznanie samego wyszukiwania za zasób. Następnie możesz użyć czasownika POST, ponieważ tworzysz wyszukiwanie. Nie musisz dosłownie tworzyć czegoś w bazie danych, aby użyć testu POST.
Na przykład:
Tworzysz wyszukiwanie z punktu widzenia użytkownika. Szczegóły implementacji tego są nieistotne. Niektóre interfejsy API RESTful mogą nawet nie wymagać trwałości. To szczegół implementacji.
źródło
Jeśli używasz treści żądania w żądaniu GET, łamiesz zasadę REST, ponieważ twoje żądanie GET nie będzie mogło być buforowane, ponieważ system pamięci podręcznej używa tylko adresu URL.
Co gorsza, Twojego adresu URL nie można dodać do zakładek, ponieważ nie zawiera on wszystkich informacji potrzebnych do przekierowania użytkownika na tę stronę
Użyj parametrów adresu URL lub zapytania zamiast parametrów treści żądania.
na przykład:
W rzeczywistości HTTP RFC 7231 mówi, że:
Ładunek w komunikacie żądania GET nie ma zdefiniowanej semantyki; wysłanie treści ładunku na żądanie GET może spowodować, że niektóre istniejące implementacje odrzucą żądanie.
Aby uzyskać więcej informacji, spójrz tutaj
źródło
Wygląda na to, że filtrowanie / wyszukiwanie zasobów można wdrożyć w sposób RESTful. Chodzi o to, aby wprowadzić nowy punkt końcowy o nazwie
/filters/
lub/api/filters/
.Korzystanie z tego filtra punktu końcowego może być traktowane jako zasób, a zatem tworzone za pomocą
POST
metody. W ten sposób - oczywiście - body można wykorzystać do przenoszenia wszystkich parametrów, a także można tworzyć złożone struktury wyszukiwania / filtrów.Po utworzeniu takiego filtra istnieją dwie możliwości uzyskania wyniku wyszukiwania / filtrowania.
Nowy zasób z unikalnym identyfikatorem zostanie zwrócony wraz z
201 Created
kodem statusu. Następnie za pomocą tego identyfikatoraGET
można poprosić o/api/users/
polubienie:Po utworzeniu nowego filtra za
POST
jego pośrednictwem nie będzie odpowiadać,201 Created
ale jednocześnie303 SeeOther
zLocation
nagłówkiem wskazującym na/api/users/?filterId=1234-abcd
. To przekierowanie będzie obsługiwane automatycznie przez bibliotekę bazową.W obu przypadkach konieczne jest przesłanie dwóch żądań, aby uzyskać filtrowane wyniki - może to być uważane za wadę, szczególnie w przypadku aplikacji mobilnych. W przypadku aplikacji mobilnych użyłbym pojedynczego
POST
połączenia z/api/users/filter/
.Jak zachować utworzone filtry?
Mogą być przechowywane w DB i używane później. Mogą być również przechowywane w tymczasowym magazynie, np. Redis i mają trochę czasu wygaśnięcia, po upływie którego wygasną i zostaną usunięte.
Jakie są zalety tego pomysłu?
Filtry, przefiltrowane wyniki są buforowane i można je nawet dodać do zakładek.
źródło
Myślę, że powinieneś iść z parametrami żądania, ale tylko tak długo, dopóki nie ma odpowiedniego nagłówka HTTP, aby osiągnąć to, co chcesz zrobić. Specyfikacja HTTP nie mówi wprost, że GET nie może mieć treści. Jednak ten artykuł stwierdza:
źródło
Kiedy używam backendu laravel / php, mam tendencję do korzystania z czegoś takiego:
PHP automatycznie przekształca parametry
[]
w tablicę, więc w tym przykładzie skończę ze$filter
zmienną, która przechowuje tablicę / obiekt filtrów, wraz ze stroną i wszelkimi powiązanymi zasobami, które chcę załadować.Jeśli używasz innego języka, może to być dobra konwencja i możesz utworzyć analizator składni w celu konwersji
[]
na tablicę.źródło
[
i]
. Używanie zakodowanych reprezentacji tych znaków do grupowania parametrów zapytań jest dobrze znaną praktyką. Jest nawet używany w specyfikacji JSON: API .Nie przejmuj się zbytnio, jeśli początkowy interfejs API jest w pełni RESTful, czy nie (szczególnie, gdy jesteś właśnie w fazie alfa). Najpierw uruchom hydraulikę zaplecza. Zawsze możesz przeprowadzić transformację / ponowne zapisanie adresu URL, aby zmapować wszystko, dopracowując iteracyjnie, dopóki nie uzyskasz czegoś wystarczająco stabilnego do powszechnego testowania („beta”).
Możesz zdefiniować identyfikatory URI, których parametry są kodowane przez pozycję i konwencję na samych identyfikatorach URI, poprzedzone ścieżką, o której wiesz, że zawsze będziesz na nią mapować. Nie znam PHP, ale zakładam, że istnieje taka możliwość (tak jak w innych językach z frameworkiem):
.to znaczy. Wykonaj wyszukiwanie typu „użytkownik” z param [i] = wartość [i] dla i = 1..4 w sklepie nr 1 (z wartością1, wartość2, wartość3, ... jako skrót dla parametrów zapytania URI):
lub
lub w następujący sposób (chociaż nie poleciłbym tego, więcej o tym później)
Dzięki opcji 1 mapujesz wszystkie identyfikatory URI z prefiksem
/store1/search/user
na moduł obsługi wyszukiwania (lub dowolne oznaczenie PHP), domyślnie wykonując wyszukiwanie zasobów w magazynie 1 (równoważne z/search?location=store1&type=user
.Zgodnie z konwencją udokumentowaną i wymuszoną przez API wartości parametrów od 1 do 4 są oddzielane przecinkami i prezentowane w tej kolejności.
Opcja 2 dodaje typ wyszukiwania (w tym przypadku
user
) jako parametr pozycyjny nr 1. Każda z tych opcji jest tylko wyborem kosmetycznym.Opcja 3 jest również możliwa, ale nie sądzę, żebym chciał. Myślę, że zdolność wyszukiwania w ramach niektórych zasobów powinna być prezentowana w samym URI poprzedzającym samo wyszukiwanie (tak jakby wyraźnie wskazywał w URI, że wyszukiwanie jest specyficzne w obrębie zasobu).
Zaletą tego nad przekazywaniem parametrów w URI jest to, że wyszukiwanie jest częścią URI (w ten sposób traktując wyszukiwanie jako zasób, zasób, którego treść może - i będzie - zmieniać się w czasie). Wadą jest to, że kolejność parametrów jest obowiązkowa .
Gdy zrobisz coś takiego, możesz użyć GET, a byłby to zasób tylko do odczytu (ponieważ nie możesz POST lub PUT do niego - jest aktualizowany, gdy jest GET'ed). Byłby to również zasób, który powstaje dopiero po wywołaniu.
Można również dodać do niego więcej semantyki, buforując wyniki przez pewien czas lub USUŃ powodując usunięcie pamięci podręcznej. Może to jednak być sprzeczne z tym, do czego ludzie zwykle używają DELETE (i ponieważ ludzie zwykle kontrolują buforowanie za pomocą nagłówków buforowania).
Sposób, w jaki sobie poradzisz, byłby decyzją projektową, ale tak właśnie bym sobie podjął. To nie jest idealne i jestem pewien, że będą przypadki, w których robienie tego nie jest najlepszą rzeczą (szczególnie w przypadku bardzo złożonych kryteriów wyszukiwania).
źródło
FYI: Wiem, że to trochę za późno, ale dla każdego, kto jest zainteresowany. W zależności od tego, jak RESTful chcesz być, będziesz musiał wdrożyć własne strategie filtrowania, ponieważ specyfikacja HTTP nie jest w tym bardzo jasna. Chciałbym zaproponować kodowanie adresu URL wszystkich parametrów filtra, np
Wiem, że to brzydkie, ale myślę, że to najbardziej ODPOCZYNY sposób, aby to zrobić i powinien być łatwy do przeanalizowania po stronie serwera :)
źródło