Punkt końcowy REST, aby wyświetlić podgląd przed testem POST

17

Projektuję nową aplikację internetową, która jest oparta na zapleczu REST i nakładce HTML + JS.

Jest na nim jedna metoda POST , aby zmienić jeden byt (nazwijmy Config), który ma kilka skutków ubocznych w stanie wielu elementów aplikacji. Załóżmy, że test POST jest wykonywany w ten sposób:

POST /api/config BODY {config: ....}

Z tego powodu chciałbym wyświetlić podgląd przed wprowadzeniem tych zmian, aby użytkownik końcowy mógł zauważyć, co się zmieni.

Pierwszą rzeczą, o której pomyślałem, było utworzenie punktu końcowego GET dla podglądu, wysyłając treść nowego stanu encji. Tą drogą:

GET /api/preview/items BODY {config: ....}

Może pokazywać nowy stan elementów z nową konfiguracją.

GET /api/preview/sales BODY {config: ....}

Może pokazać nowy stan sprzedaży dzięki nowej konfiguracji.

Dobrym pomysłem wydaje się użycie czasownika GET, ponieważ nie zmieniam stanu aplikacji. Jednak stosowanie treści żądania z żądaniami GET wydaje się być odradzane .

Czy jest w tym jakaś dobra praktyka? Innym wyborem może być przechowywanie konfiguracji jako wersji roboczej za pomocą jednej metody i wyświetlanie wyników z innymi, ale wymagałoby to dodatkowego kroku i konieczności zarządzania wersjami roboczymi na serwerze:

POST /api/preview/config BODY {config: ....}

GET /api/preview/items?idPreviewConfig=1
Xtreme Biker przywraca Monikę
źródło
Czym dokładnie może być ta konfiguracja i jak wpływa na itemslub sales? Czy wpływa to na reprezentację zwracanego podmiotu?
Andy
Załóżmy, że na zmiany zarówno pozycji, jak i sprzedaży wpływają zmiany dokonane w konfiguracji.
Xtreme Biker przywraca Monikę
Ale co oznaczają zmiany? Czy zmienia zestaw zwracanych encji? Czy zmienia zwracaną strukturę?
Andy,
W rzeczywistości zmienia wartości itemsi sales( a nie strukturę), w zależności od konfiguracji POST.
Xtreme Biker przywraca Monikę
A jak duża jest konfiguracja? Czy dorasta do kilkuset kilobajtów, a nawet więcej?
Andy

Odpowiedzi:

27

Jest to zbyt specyficzne dla domeny, aby mieć macierzystą obsługę HTTP.

Zamiast tego możesz wykonać jedną z następujących czynności:

  1. Mają POST /api/config/preview. Po stronie serwera aplikacja będzie wiedziała, że ​​nie powinna modyfikować faktycznej konfiguracji, ale połączyć rzeczywistą z opublikowaną i zwrócić wynik wskazujący, co zostało zmienione.

    Później, jeśli użytkownik będzie zadowolony z wyniku, wykona POST /api/configzawierający taki sam ładunek, jak w poprzednim żądaniu. Spowoduje to skuteczne zastąpienie konfiguracji.

    Zaletą tego podejścia jest to, że nie wprowadzasz żadnych przełomowych zmian w bieżącym interfejsie API. Klienci, którzy nie potrzebują funkcji podglądu, nadal będą mogli aktualizować wpisy, tak jak wcześniej.

    Wadą jest to, że gdy ciało jest duże, oznacza to, że konieczne będzie dwukrotne wysłanie go na serwer. Jeśli tak jest w twoim przypadku, możesz zastosować następne podejście.

  2. Miej, POST /api/config/preparektóry pamięta, co zostało wysłane w tymczasowym rekordzie i zwraca dwie rzeczy: identyfikator rekordu tymczasowego (na przykład 12345) i podgląd zmian.

    Jeśli użytkownik jest zadowolony z wyniku, wykona a, POST /api/config/commit/12345aby ostatecznie zapisać zmiany. Jeśli nie, tymczasowy zapis może być przechowywany przez pewien czas, a następnie odrzucony przez zadanie cron.

    Zaletą jest to, że tutaj znowu możesz zachować POST /api/confignienaruszony oryginał , a klienci, którzy nie potrzebują podglądu, nie ulegną awarii.

    Wady polegają na tym, że (1) obsługa usuwania tymczasowych rekordów może być trudna (co sprawia, że ​​myślisz, że jedna godzina wystarczy? Co jeśli dziesięć minut później zabraknie pamięci? Jak klienci obsługują HTTP 404 podczas wykonywania zatwierdzenia rekord, który wygasł?) i że (2) dwuetapowe przesłanie rekordu może być bardziej skomplikowane, niż to konieczne.

  3. Przenieś logikę podglądu po stronie klienta.

Arseni Mourzenko
źródło
A co z wysłaniem nagłówka z napisem „nie utrzymuj tego, pokaż mi tylko co-jeśli”? Przeredaguję to w odpowiedzi, jeśli nie masz nic
przeciwko
1
@marstato: osobiście nie przepadam za nagłówkami HTTP do takich zastosowań. Chociaż może to mieć sens dla innych osób, więc w porządku, jeśli edytujesz moją odpowiedź. Pamiętaj, że możesz również opublikować własną odpowiedź, która pozwoli innym głosować za nią (i zapewni ci punkty reputacji).
Arseni Mourzenko
Chyba opcja 1 lepiej pasuje do mojego przypadku. Więc POST konfigurujesz podgląd i masz zmiany w wyniku, zamiast konieczności definiowania punktów końcowych podglądu dla każdego ze zdefiniowanych encji. Wydaje się rozsądne. Technicznie rzecz biorąc, używasz POST, aby nie wprowadzać żadnych zmian na serwerze. Opcja 3 jest nieopłacalna w moim przypadku.
Xtreme Biker przywraca Monikę
1
@PedroWerneck Czy możesz to rozwinąć? Wydaje mi się, że opcja 2 definiuje inny byt (szkic konfiguracji) i zapewnia bezpaństwowe sposoby interakcji z nimi.
Andrew mówi Przywróć Monikę
1
@PedroWerneck Jest stanowy, podobnie jak przechowywanie konfiguracji na serwerze jest stanowe. Więc aplikacja jest już stanowa z twojej perspektywy, podobnie jak wszystkie opcje dodania tej funkcji.
jpmc26
10

Używanie określonych czasowników HTTP do różnych wywołań API w REST polega na wykorzystaniu istniejącej mechaniki i oczekiwań HTTP.

Użycie GET w tym przypadku wydaje się być sprzeczne z obiema.

A. Klient musi dołączyć ciało z GET? niespodziewany

B. Serwer zwraca inną odpowiedź na get w zależności od treści? łamie specyfikację i mechanizmy buforowania

Jeśli zmagasz się z ODPOWIEDNIMI pytaniami, moją zasadą jest zadać sobie pytanie.

„Jak to jest lepsze niż używanie POST do wszystkiego?”

O ile nie ma natychmiastowej i oczywistej korzyści, wybierz strategię Just Use POST Stupid (JUPS)

Ewan
źródło
Hahaha dobry haczyk
Xtreme Biker przywraca Monikę
@Ewan ... niezależnie od tego, czy jest to podejście pragmatyczne ... jeśli używasz POST do wszystkiego, należy zauważyć, że tak naprawdę nie jest to ODPOCZYNEK.
Allenph
1
cóż, chyba że POST jest właściwym wyborem dla wszystkich twoich metod. I nie jest tak, że istnieje obiektywna zasada, którą można zastosować, po prostu argumentowalibyśmy nasze subiektywne interpretacje czegoś, co jest niczym więcej niż tylko wytyczną.
Ewan
6

Możesz wysłać nagłówek, który wskazuje serwerowi: „nie utrzymuj tego, pokaż mi tylko, jaki byłby wynik, gdybyś to zrobił”. Na przykład

POST /api/config HTTP/1.1
Host: api.mysite.com
Content-Type: application/json
Persistence-Options: simulate

{
   "config": {
      "key": "value"
   }
}

Na który serwer może odpowiedzieć:

HTTP/1.1 200 OK
Persistence-Options: simulated
Content-Type: application/json

-- preview --

Pamiętaj, że jeśli korzystasz z DB w oparciu o transakcje O / RM i / lub transakcje na żądanie, możesz łatwo wdrożyć tę funkcjonalność dla wszystkich swoich punktów końcowych, nie wymagając pracy na żadnym konkretnym punkcie końcowym: Jeśli żądanie przychodzi z tą opcją , wycofaj transakcję / jednostkę pracy zamiast ją zatwierdzać.

marstato
źródło
@PeterRader dobry punkt, usuniętoX-
marstato
Proszę bardzo. Czy powiedziałbyś, że symulacja będąca przedmiotem symulacji powinna być reprezentowana jako „symulacja”?
Peter Rader,
Nie; o to chodzi w symulacji, prawda? Wartość nagłówka może być również, noneale - jak na mój gust - zbyt mocno przeczy to naturze POSTmetody.
marstato
2

Proponuję potraktować to tak samo, jak traktujesz wyszukiwania. Ustawiłbym punkt końcowy POST, w /api/config/previewktórym TWORZY nowy podgląd. Następnie ustawiłbym punkt końcowy PUT lub PATCH w api/configzależności od tego, czy zamierzasz edytować bieżącą konfigurację, czy po prostu zastąpić całą konfigurację (przypuszczalnie w pierwszym przypadku wysyłałeś właśnie utworzony podgląd).

Allenph
źródło
0

Oprócz innych dobrych odpowiedzi, inną opcją może być opublikowanie konfiguracji, jak wspomniano, i mieć również dostęp do procesu wycofywania. Myślę, że podobnie jak metodologia Agile, lepiej być mniej wystraszonym zmianami dzięki bardziej szczegółowym, powtarzalnym i przetestowanym procedurom, a to dałoby ci kopię zapasową, gdy jej potrzebujesz, zmniejszając ryzyko do niewielkiego lub żadnego, zależnie od aplikacji .

Z drugiej strony, jeśli możesz mieć błędy konfiguracji wpływające na cały system, chciałbyś obsłużyć go bardziej aktywnie, a jeśli tak, to dlaczego nie po prostu włożyć wysiłku w podgląd zmian w tym momencie, z perspektywy serwera lub klienta. Chociaż widzę, jak ta funkcja podglądu może być droższa w opracowaniu, przypadki użycia mają własny zestaw różnych kroków do wykonania i przetestowania.

Pysis
źródło
0

RFC6648 zastępuje nowe X-konstrukcje, więc muszę głosować przeciwko pomysłowi wysłania nowego pola nagłówka. REST to styl architektury, o którym mówimy, jest RESTful - ale na razie zignorujmy to.

Ponieważ REST jest reprezentatywny (a symulacja nie ma reprezentacji w rzeczywistości) i stanowy (a symulacja nie jest stanem do czasu jego zatwierdzenia), musimy mieć nowy zakres, na przykład zakres symulacji. Ale musimy to nazwać emulacją zamiast symulacji, ponieważ symulacja obejmuje proces symulacji, ale stanowy oznacza, że ​​mamy stan stały, idealne rozwiązanie symulacji: emulacja. Musimy więc nazwać to emulacją w adresie URL. To może być również dobre rozwiązanie:

GET  /api/emulation - 200 OK {first:1, last:123}
POST /api/emulation/124 - 200 OK
GET  /api/emulation/124/config - 200 OK {config:{tax:8}}
PUT  /api/emulation/124/config {config:{tax:16}} - 200 OK {config:{tax:16}}
GET  /api/emulation/124/items - 200 OK [first:1, last: 3000]
GET  /api/emulation/124/items/1 - 200 OK {price:1.79, name:'Cup'}
--- show emulation ---
--- commit emulation ---
PUT /api/config {config:{tax:16}}
DELETE /api/emulation/124 - 200 OK

Istnieje inne podejście ... możesz zauważyć, że wiele żądań z klienta HTML / JavaScript może wygenerować zbyt wiele żądań , co osiąga limit około 17 żądań jednocześnie (spójrz na tę stronę ). Możesz zamienić użycie REST i zamiast dostarczać słabe stany obiektowe, możesz dostarczać bogate stany stron specyficzne dla użytkownika. Przykład:

GET /user/123/config - 200 OK {user:'Tim', date:3298347239847, currentItem:123, 
                  roles:['Admin','Customer'], config:{tax:16}, unsavedChanges:true, ...}

Z poważaniem

Peter Rader
źródło