Jaki jest właściwy sposób wykonania złożonej metody wyszukiwania RESTful?

44

Zgodnie z zasadami REST chciałbym utworzyć metodę GET dla mojego interfejsu API, która przeprowadzi wyszukiwanie przy użyciu niektórych kryteriów i zwróci wyniki klientowi. Problem polega na tym, że kryteria mogą mieć do 14 parametrów, jednym z nich jest lista złożonych obiektów, więc ...

  • Nie wiem nawet, czy można zakodować / zdekodować te złożone obiekty do / z parametrów adresu URL.

  • Nie obliczyłem, jak długi może być adres URL, ale jestem pewien, że będzie on wystarczająco duży i być może osiągnie limit długości adresu URL?

Ponadto wyszukiwanie powinno pokazywać wyniki w „czasie rzeczywistym”, to znaczy za każdym razem, gdy użytkownik zmienia coś z formularza wyszukiwania, powinien być w stanie zobaczyć nowe wyniki bez naciskania żadnego przycisku „wyszukiwania”.

Czy mógłbyś mi wyjaśnić te kwestie i jaka byłaby twoja rada, aby stworzyć metodę wyszukiwania opartą na wielu parametrach?

anat0lius
źródło
3
Na marginesie (zauważam, że żadna z dwóch odpowiedzi w momencie pisania nie wspomina o części w czasie rzeczywistym), wyszukiwania w czasie rzeczywistym zwykle powinny po prostu wysyłać zaktualizowane żądanie w kółko. Na przykład podczas pisania, byłbyś wysyłania żądań do rzeczy jak search?q=t, search?q=te, search?q=testi tak dalej. Rozważ ograniczenie częstotliwości wysyłania zapytania, aby uniknąć uszkodzenia serwera. Możesz również zwrócić wiele informacji i po stronie klienta przeprowadzić filtrowanie. Działa to dobrze, jeśli użytkownik wprowadza szerokie kategorie, które mogą znacznie zawęzić zakres.
Kat
2
Bez względu na rozwiązanie pamiętaj o wymianie danych w neutralnym technologicznie formacie. Więc nie używaj wewnętrznej reprezentacji, bo trudniej będzie napisać klientów dla twojego API.
Kwebble
W zależności od potrzeb ta biblioteka może wykonać zadanie: github.com/jirutka/rsql-parser . Ma rozszerzenie JPA, jeśli używasz JPA, lub możesz napisać własnego gościa, aby zmapować go do interfejsu API swojej wyszukiwarki. I możesz dodać własnego operatora.
Walfrat

Odpowiedzi:

54

Zanim przeczytasz moją odpowiedź, chciałbym powiedzieć, że zgodziłem się z @Neil. Musimy wybrać nasze bitwy. Zwykle chcemy dać z siebie wszystko, ale czasem jest za mało miejsca na dyskusje i musimy podejmować decyzje wbrew naszej woli.

W każdym razie, w odpowiedzi Neila, brakuje mi jeszcze jednej rzeczy. Dokumentacja . Aby upewnić się, że programiści wiedzą, że żądania POST /searchsą bezpieczne.

To mówi.

1. Daj szansę GET

GETNajpierw rozważ opcję. Sprawdź maksymalną długość adresu URL tego pytania . Oceń, czy najdłuższy ciąg zapytania ma więcej niż 2000 znaków. Jeśli tak nie jest i nie spodziewasz się, że tak będzie, idź z GET. Może się to wydawać brzydkie, ale przynajmniej możesz dodać do zakładek adres URL i, oczywiście, ma wszystkie zalety wynikające z semantyki metody (idempotencja, bezpieczeństwo i buforowanie)

1.1 Spróbuj zakodować ciąg zapytania

Na przykład w bazie 64. Nawet javascript obsługuje kodowanie bazy 64 .

Oto jak to działa:

  1. Zbuduj JSON ze wszystkimi filtrami i znormalizuj go.
  2. Parsuj to na string
  3. Zakoduj to
  4. Wyślij zakodowany JSON jako żądanie param ( /search?q=SGVsbG8gV29ybGQh....).
  5. Po stronie serwera zdekoduj parametr q .
  6. Deserializuj ciąg JSON

Wcześniej wykonaj najdłuższy możliwy ciąg JSON, zakoduj go i przyjmij długość. Oceń, czy zakodowany ciąg pasuje do adresu URL. Zaimplementowałem następujący fragment kodu w Fiddle.js do przetestowania. (Mam nadzieję, że nadal działa) 1

Podstawowe kody 64 są deterministyczne i odwracalne, więc nie ma szans na kolizje.

Za pomocą zakodowanych zapytań możemy również zapisywać wyszukiwania w bazie danych, dodawać do zakładek adres URL, udostępniać linki itp. I oczywiście nie musimy uciekać / usuwać scen z łańcucha.

1.2 Spróbuj użyć aliasów

Czytając ten blog na temat projektowania interfejsów API REST, przypomniałem sobie jeszcze jedną alternatywę. Aliasy dla typowych zapytań .

Uważam, że są interesujące z następujących powodów

  • Skróć długość ciągu zapytania. Dzięki temu interfejs API jest czystszy i przyjazny dla użytkownika

    GET / bilety / status = zamknięte i zamknięte At = xxx vs GET / bilety / ostatnio zamknięte /

  • Można łączyć z większą liczbą aliasów lub większą liczbą parametrów żądania.

    GET / bilety / status = zamknięte i zamknięte At = xxx i w ciągu = 30 minut vs GET / bilety / ostatnio zamknięte / w ciągu = 30 minut

  • Możemy łączyć aliasy z zakodowanymi ciągami zapytań

    GET / bilety /? Status = zamknięte i zamknięte At = xxx i w ciągu = 30 min vs GET / bilety / ostatnio zamknięte /? Q = SGVsbG8g ...


1: Użyłem JSON, ale moglibyśmy użyć innych formatów, jak tylko moglibyśmy dokonać deserializacji po stronie serwera.

Laiv
źródło
2
Jest to zarówno praktyczne, jak i prawidłowe. Warto również zauważyć, że większość języków programowania sprawia, że ​​przekształcanie skrótu w ciąg zapytania jest banalne, więc rozpoczęcie od działania GET jest bardzo łatwe.
Aluan Haddad
1
Uwielbiam Spring stackoverflow.com/questions/16942193/... Nie mogę uwierzyć, że zadziałało za pierwszym razem: D. Informacje o długości adresu URL wynoszą mniej niż 1 tys., Choć nadal musimy iterować specyfikacje.
anat0lius
Następnie przejdź z GET. Dla prostoty. Dzięki Spring MVC możesz uzyskać to samo mapowanie za pomocą GET. Spójrz na WebArgumentResolver wiosennego ;-)
LAIV
Base64 napełnia rozmiar ładunku o około 4/3. Chociaż urlencoding może sprawić, że będzie to 3/1 dla znaków specjalnych, zapytania z przeważnie bezpiecznymi znakami będą miały ten sam rozmiar. Czy jest jakiś inny powód, aby używać base64?
villasv
Nie całkiem. Po prostu nie lubię (nie) ucieczkowych adresów URL. Przeważenie ładunku jest tutaj kompromisem. Nadal musi mieścić się w maksymalnym rozmiarze GET na żądanie. Właśnie dlatego zbudowałem fragment kodu. Do wypróbowania przez użytkownika. Kiedy napisałem odpowiedź, nadałem priorytet semantyce internetowej nad szczegółami implementacji. Sedno odpowiedzi brzmi: „próbuj z GET”. Znajdź swoją drogę lub skorzystaj z tych, które Ci udostępniam.
Laiv
13

Jeśli masz tylko młotek, wszystko wygląda jak gwóźdź. Wygląda na to, że problem polega na tym, że próbujesz zamienić stronę wyszukiwania w stronę RESTful, a to raczej nie jest powszechny wzór do rozwiązania przy projektowaniu RESTful.

Po prostu przejdź do żądania POST z parametrami podanymi przez użytkownika, aby uzyskać wymagane informacje z backendu. Zakładam, że nie musisz nic robić poza wyszukiwaniem, więc nie ma szans, że będziesz musiał wstawić tę stronę. Wystarczy dodać opcję / search na końcu swojego adresu URL, aby nie ryzykować konfliktów ze stroną / users, które byłyby RESTful.

Neil
źródło
2
@LiLou_: W przypadku tego wymogu istnieją tylko dwie realistyczne możliwości: 1. Przeczytaj wszystkie dane z interfejsu użytkownika i przeprowadź filtrowanie. Może to być zbyt wysokie w wymaganej ilości pamięci. 2. Wykonaj nowe żądanie do serwera dla każdej zmiany kryteriów wyszukiwania. Nie ma znaczenia, czy jest to żądanie POST, czy GET, ale związane z tym opóźnienie sieci może być zakłócające dla użytkownika w odczuciu aktualizacji „w czasie rzeczywistym”.
Bart van Ingen Schenau
2
Zgodziłbym się nie zgodzić, POST oznacza coś semantycznie. Sugerowałbym pójść z GET i przekazać teraz wszystkie dane filtru w parametrze zapytania, jeśli jest zbyt wiele parametrów, to jest to błąd na poziomie aplikacji.
CodeYogi,
2
@CodeYogi czasami klient nie daje ci miejsca na dyskuzję. Zaimplementowałem strony widoku podobne do Excela z 40-50 kolumnami, każda z własnym filtrem. I oczywiście do sortowania. W każdym razie jest to możliwe z GET, ale może nie wydawać się zbyt modne
Laiv
1
@Laiv w takim przypadku musi być poważna dyskusja, ponieważ POST jest przeznaczony do zmiany stanu serwera. Takie przypadki użycia nie są wyjątkowe, dlatego należy się nimi zająć bez włamań.
CodeYogi,
2
W tych przypadkach dokumentacja jest koniecznością. Przeprowadziłem poważną dyskusję z klientami na temat użyteczności ich aplikacji. Później okazało się, że miałem rację, ponieważ użytkownik końcowy zgodził się ze mną. Czasami jednak musisz wybrać swoje bitwy.
Laiv
0

To w pełni zależy od tego, jaki jest twój model API: jako brak lub jako czasownik.

Jeśli API nie ma, możesz chcieć uzyskać listę obiektów w następujący sposób:

GET: /api/v1/objects

W takim przypadku musisz wysłać dane jako parametry żądania. Musisz więc opisać swoje parametry jako płaską listę kluczowych wartości:

GET: /api/v1/objects

key1 : val1
key2.key1 : val 21
key2.key2 : val 22
....

Niektóre platformy obsługują niestandardowe narzędzie do rozwiązywania parametrów (np. Spring MVC), w którym można konwertować parametry na obiekt.

Mostafa
źródło