Jakie sprawdzone wzorce projektowe istnieją dla operacji wsadowych na zasobach w ramach usługi sieci Web w stylu REST?
Staram się znaleźć równowagę między ideałami a rzeczywistością pod względem wydajności i stabilności. Mamy teraz API, w którym wszystkie operacje pobierają z zasobu listy (tj: GET / user) lub na pojedynczej instancji (PUT / user / 1, DELETE / user / 22, itd.).
W niektórych przypadkach chcesz zaktualizować jedno pole z całego zestawu obiektów. Przesyłanie całej reprezentacji każdego obiektu tam iz powrotem w celu zaktualizowania jednego pola wydaje się bardzo marnotrawstwem.
W interfejsie API w stylu RPC możesz mieć metodę:
/mail.do?method=markAsRead&messageIds=1,2,3,4... etc.
Jaki jest tutaj odpowiednik REST? A może od czasu do czasu można pójść na kompromis. Czy dodanie kilku konkretnych operacji, w których naprawdę poprawia wydajność, itp. Psuje projekt? Klientem we wszystkich przypadkach jest obecnie przeglądarka internetowa (aplikacja javascript po stronie klienta).
źródło
PATCH
- w tym przypadku nie ma potrzeby kreatywności.Wcale nie - myślę, że odpowiednik REST to (lub przynajmniej jedno rozwiązanie jest) prawie dokładnie tym - wyspecjalizowany interfejs zaprojektowany z myślą o operacji wymaganej przez klienta.
Przypomina mi się wzorzec wspomniany w książce Crane'a i Pascarello Ajax in Action (nawiasem mówiąc, znakomita książka - bardzo polecana), w której ilustrują one implementację obiektu typu CommandQueue, którego zadaniem jest kolejkowanie żądań w partie i następnie wysyłaj je okresowo na serwer.
Obiekt, o ile dobrze pamiętam, zawierał po prostu tablicę "poleceń" - np. Aby rozszerzyć twój przykład, każdy z nich zawierał polecenie "markAsRead", "messageId" i być może odwołanie do wywołania zwrotnego / handlera function - a następnie zgodnie z harmonogramem lub działaniem użytkownika obiekt polecenia byłby serializowany i wysyłany do serwera, a klient zajmowałby się następczym przetwarzaniem końcowym.
Nie mam pod ręką szczegółów, ale wygląda na to, że kolejka poleceń tego rodzaju byłaby jednym ze sposobów rozwiązania problemu; znacznie zmniejszyłoby to ogólną rozmowę i wyodrębniłoby interfejs po stronie serwera w sposób, który może okazać się bardziej elastyczny w przyszłości.
Aktualizacja : Aha! Znalazłem w Internecie wycinek z tej książki, wraz z próbkami kodu (chociaż nadal sugeruję zakup właściwej książki!). Zajrzyj tutaj , zaczynając od sekcji 5.5.3:
Oto dwie istotne funkcje - jedna odpowiedzialna za dodawanie poleceń do queue (
addCommand
), a druga za serializację i wysyłanie ich do serwera (fireRequest
):To powinno cię skłonić do działania. Powodzenia!
źródło
Chociaż myślę, że @Alex jest na dobrej drodze, myślę, że koncepcyjnie powinno być odwrotnością tego, co jest sugerowane.
Adres URL to w efekcie „zasoby, na które kierujemy reklamy”, stąd:
oznacza pobranie rekordu z poczty o identyfikatorze 1 i
oznacza załatanie rekordu poczty o id 1. Zapytanie jest „filtrem” filtrującym dane zwracane z adresu URL.
Więc tutaj prosimy o wszystkie wiadomości już oznaczone jako przeczytane. Zatem [PATCH] na tej ścieżce oznaczałoby „załataj rekordy już oznaczone jako prawdziwe”… co nie jest tym, co próbujemy osiągnąć.
Tak więc metoda wsadowa, zgodnie z tym myśleniem, powinna być:
oczywiście nie mówię, że to jest prawdziwy REST (który nie pozwala na manipulowanie rekordami wsadowymi), raczej jest zgodny z logiką już istniejącą i używaną przez REST.
źródło
[GET]
formatem do zrobienia[PATCH] mail?markAsRead=true data: [{"id": 1}, {"id": 2}, {"id": 3}]
(lub nawet po prostudata: {"ids": [1,2,3]}
)? Inną korzyścią płynącą z tego alternatywnego podejścia jest to, że nie wystąpią błędy „414 Żądania URI zbyt długie”, jeśli aktualizujesz setki / tysiące zasobów w kolekcji.Twój język, „ Wydaje się bardzo marnotrawny…”, wskazuje mi na próbę przedwczesnej optymalizacji. O ile nie można wykazać, że wysyłanie całej reprezentacji obiektów jest dużym spadkiem wydajności (mówimy o nie do przyjęcia dla użytkowników jako> 150 ms), nie ma sensu próbować tworzyć nowego niestandardowego zachowania API. Pamiętaj, im prostsze API, tym łatwiejsze w użyciu.
W przypadku usuwania wyślij następujące informacje, ponieważ serwer nie musi nic wiedzieć o stanie obiektu przed usunięciem.
Następna myśl jest taka, że jeśli aplikacja ma problemy z wydajnością dotyczące zbiorczej aktualizacji obiektów, należy rozważyć podział każdego obiektu na wiele obiektów. W ten sposób ładunek JSON jest ułamkiem rozmiaru.
Na przykład podczas wysyłania odpowiedzi w celu zaktualizowania stanu „przeczytane” i „zarchiwizowane” dwóch oddzielnych wiadomości e-mail należy wysłać następujące informacje:
Rozdzieliłbym zmienne składniki wiadomości e-mail (odczyt, zarchiwizowanie, ważność, etykiety) na osobny obiekt, ponieważ inne (do, od, temat, tekst) nigdy nie byłyby aktualizowane.
Innym podejściem, które można zastosować, jest wykorzystanie PATCH. Wyraźne wskazanie, które właściwości zamierzasz zaktualizować, a wszystkie inne należy zignorować.
Ludzie twierdzą, że PATCH należy zaimplementować, dostarczając tablicę zmian zawierającą: akcję (CRUD), ścieżkę (URL) i zmianę wartości. Można to uznać za standardową implementację, ale jeśli spojrzeć na całość REST API, jest to nieintuicyjne rozwiązanie jednorazowe. Ponadto powyższa implementacja to sposób, w jaki GitHub zaimplementował PATCH .
Podsumowując, możliwe jest przestrzeganie zasad RESTful z akcjami wsadowymi i nadal mieć akceptowalną wydajność.
źródło
Interfejs API dysku Google ma naprawdę ciekawy system rozwiązania tego problemu ( patrz tutaj ).
Zasadniczo grupują różne żądania w jednym
Content-Type: multipart/mixed
żądaniu, przy czym każde indywidualne pełne żądanie jest oddzielone określonym separatorem. Nagłówki i parametry zapytania w żądaniu wsadowym są dziedziczone do poszczególnych żądań (tj.Authorization: Bearer some_token
), Chyba że zostaną nadpisane w indywidualnym żądaniu.Przykład : (pobrane z ich dokumentów )
Żądanie:
Odpowiedź:
źródło
W operacji takiej jak ta z twojego przykładu kusiłabym się, aby napisać parser zakresu.
Stworzenie parsera, który potrafi odczytać „MessageIds = 1-3,7-9,11,12-15” nie jest zbyt trudne. Z pewnością zwiększyłoby to wydajność ogólnych operacji obejmujących wszystkie wiadomości i jest bardziej skalowalne.
źródło
Wspaniały post. Szukałem rozwiązania od kilku dni. Wymyśliłem rozwiązanie polegające na przekazaniu ciągu zapytania z identyfikatorami grup oddzielonych przecinkami, na przykład:
... a następnie przekazanie tego do
WHERE IN
klauzuli w moim SQL. Działa świetnie, ale zastanawiam się, co inni myślą o tym podejściu.źródło
DELETE /books/delete?id=1,2,3
gdy książka nr 3 nie istnieje - poWHERE IN
cichu zignoruje rekordy, podczas gdy zwykle spodziewałbymDELETE /books/delete?id=3
się 404, jeśli 3 nie istnieje.Z mojego punktu widzenia uważam, że Facebook ma najlepszą implementację.
Wysyłane jest pojedyncze żądanie HTTP z parametrem wsadowym i jednym dla tokenu.
W partii jest wysyłany plik json. który zawiera zbiór „wniosków”. Każde żądanie ma właściwość metody (get / post / put / delete / etc ...) irelative_url property (uri of endpoint), dodatkowo metody post i put pozwalają na właściwość "body", w której pola mają być aktualizowane są wysyłane .
więcej informacji na: Facebook batch API
źródło