RESTful sposób tworzenia wielu elementów w jednym żądaniu

122

Pracuję nad małym programem serwera klienta do zbierania zamówień. Chcę to zrobić w „REST (pełny) sposób”.

Chcę tylko:

Zbierz wszystkie zamówienia (produkt i ilość) i wyślij całe zamówienie na serwer

W tej chwili widzę dwie opcje, aby to zrobić:

  1. Wyślij każdy wiersz zamówienia na serwer: POST qty i product_id

Właściwie nie chcę tego robić, ponieważ chcę ograniczyć liczbę żądań do serwera, więc opcja 2:

  1. Zbierz wszystkie zamówienia i wyślij je na serwer jednocześnie.

Jak wdrożyć opcję 2? Mam kilka pomysłów: Zawiń wszystkie linie zamówienia w obiekt JSON i wyślij to na serwer lub użyj tablicy, aby opublikować linie zamówienia.

Czy dobrym pomysłem lub dobrą praktyką jest wdrożenie opcji 2, a jeśli tak, to jak mam to zrobić.

Co to jest dobra praktyka?


źródło

Odpowiedzi:

74

Uważam, że innym właściwym sposobem rozwiązania tego problemu byłoby stworzenie innego zasobu, który reprezentuje twoją kolekcję zasobów. Przykład, wyobraź sobie, że mamy punkt końcowy taki jak /api/sheep/{id}i możemy POST w /api/sheepcelu utworzenia zasobu owiec.

Teraz, jeśli chcemy wspierać tworzenie zbiorcze, powinniśmy rozważyć nowy zasób stada pod adresem /api/flock(lub /api/<your-resource>-collectionjeśli brakuje ci lepiej zrozumiałej nazwy). Pamiętaj, że zasoby nie muszą być mapowane do Twojej bazy danych lub modeli aplikacji . To powszechne nieporozumienie.

Zasoby to reprezentacja wyższego poziomu, niezwiązana z Twoimi danymi. Operowanie na zasobie może mieć znaczące skutki uboczne, takie jak wysłanie ostrzeżenia do użytkownika, aktualizacja innych powiązanych danych, zainicjowanie długotrwałego procesu itp. Na przykład moglibyśmy zmapować system plików lub nawet pspolecenie unixa jako REST API.

Myślę, że można bezpiecznie założyć, że działanie zasobu może oznaczać również tworzenie kilku innych podmiotów jako efekt uboczny.

miguelcobain
źródło
Zgadzam się z tym. Powinieneś wyodrębnić koncepcję kolekcji swojego zasobu i traktować ją jak zasób. Zapewni to większą elastyczność również w przyszłości, kiedy zechcesz rozpocząć operacje na tym itp.
villy393
To jest właściwe podejście. Nie przerywa to żądania POST Collection. Ponieważ jest używany do publikowania pojedynczej jednostki. Wysłanie żądania zbiorczego z „oddzielną jednostką zbiorczą” jest właściwym podejściem.
Sorter
2
Bardzo podoba mi się nazywanie punktów końcowych api za pomocą owiec i stada! Z pewnym stopniem abstrakcji ma niemal biblijne odniesienie: „Mam drugie owce, które nie są z tej owczarni; muszę je także przyprowadzić, a będą słuchać mego głosu i staną się jedną trzodą z jednym pasterzem”. Jana 10:16.
Evgeny,
1
Co ciekawe, ludzie zalecają używanie liczby mnogiej (zbioru) w adresie URL, gdy chcesz utworzyć pojedynczy zasób, na przykład: wyślij POST do / api / books, aby utworzyć książkę. Ale kiedy chcesz utworzyć 100 książek (w jednym żądaniu jako json), pod który adres URL opublikowałbyś kolekcję 100 książek? tam zaczyna się niepokój.
code4kix
@ code4kix, którego możesz użyć /api/book-group, /api/book-collectionlub coś podobnego.
miguelcobain
46

Chociaż operacje zbiorcze (np. Tworzenie wsadowe) są niezbędne w wielu systemach, nie są formalnie uwzględniane w stylu architektury RESTful.

Zauważyłem, że udostępnianie kolekcji zgodnie z sugestią w zasadzie działa, ale pojawiają się problemy, gdy trzeba zgłosić awarie w odpowiedzi na takie żądanie. Takie problemy są gorsze, gdy występuje wiele awarii z różnych przyczyn lub gdy serwer nie obsługuje transakcji. Moja sugestia jest taka, że ​​jeśli nie ma problemu z wydajnością, na przykład gdy usługodawca jest w sieci LAN (nie WAN) lub dane są stosunkowo małe, warto wysłać 100 żądań POST do serwera. Zachowaj prostotę, zacznij od oddzielnych żądań, a jeśli masz problem z wydajnością, spróbuj zoptymalizować.

LiorH
źródło
3
Czy sam znalazłeś rozwiązanie błędów w przypadku wsadowania? Na połączeniu mobilnym wysyłanie 100 żądań postów w celu pokazania strony wydaje się złym pomysłem.
Thomas Ahle
Dołączam błędy do tablicy, kieruję użytkownika do strony błędów 419 Conflict (i zwracam ten błąd do klienta) i wyświetlam tablicę błędów. Zobacz moją odpowiedź poniżej, aby uzyskać więcej informacji.
Eric Fuller
5
To jest nonsens. Pytanie dotyczy wysłania zamówienia na wiele pozycji, które jak wielu powiedziało, można tylko w ramach jednego żądania POST. Sposób, w jaki serwer sobie z tym radzi, to zupełnie inna sprawa. W tym przypadku nie widzę problemu z utworzeniem zamówienia, wypełnieniem tego, co możesz dla tego zamówienia, a także wypełnieniem szczegółów, których nie można było wykonać. W ten sposób użytkownik może zobaczyć swoje zamówienie i zobaczyć, że wszystkie produkty oprócz N zostały dodane do zamówienia, ale niektóre były niedostępne lub system nie wiedział, co zrobić. Inną prostszą, ale mniej przyjazną dla użytkownika opcją jest odrzucenie wszystkiego
thecoshman
2
@thecoshman wiele się zmienia w ciągu 3,25 roku. Prawdopodobnie powinieneś zamieścić całkowicie sformułowaną odpowiedź na pytanie.
dlamblin
3
@dlamblin yeah, prawdopodobnie powinienem zrobić wiele rzeczy ... może
dojdę
9

Facebook wyjaśnia, jak to zrobić: https://developers.facebook.com/docs/graph-api/making-multiple-requests

Proste żądania grupowe

Batch API przyjmuje tablicę logicznych żądań HTTP reprezentowanych jako tablice JSON - każde żądanie ma metodę (odpowiadającą metodzie HTTP GET / PUT / POST / DELETE itp.), Względny_url (część adresu URL po graph.facebook. com), opcjonalna tablica nagłówków (odpowiadająca nagłówkom HTTP) i opcjonalna treść (dla żądań POST i PUT). Interfejs Batch API zwraca tablicę logicznych odpowiedzi HTTP reprezentowanych jako tablice JSON - każda odpowiedź ma kod stanu, opcjonalną tablicę nagłówków i opcjonalną treść (która jest ciągiem zakodowanym w formacie JSON).

rwitzel
źródło
1
To bardzo ciekawy link, zaproponowane rozwiązanie wydaje mi się przydatne. W każdym razie w StackOverflow preferowaną odpowiedzią jest wyjaśnienie pojęcia rozwiązania w treści odpowiedzi, ponieważ linki mogą się zmienić lub zniknąć.
Jan Vlcinsky
7
To naprawdę sposób na Facebooka, niekoniecznie RESTful, jak poprosił OP
0cd
Myślę, że Batch API (z Google, Facebooka itp. - @PuneetArora) jest bardziej użyteczne podczas grupowania kilku niepowiązanych żądań. Tworzenie żądania, które tworzy jeden element, a następnie grupowanie wszystkich żądań razem w celu wysłania kolekcji przedmiotów to „szaleństwo” (Einstein). Po prostu utwórz żądanie, które przekaże kolekcję elementów.
tfmontague
8

Twój pomysł wydaje mi się ważny. Realizacja zależy od Twoich preferencji. Możesz użyć do tego JSON lub po prostu parametrów (tablica „order_lines []”) i zrobić

POST /orders

Ponieważ zamierzasz stworzyć więcej zasobów naraz w jednej akcji (zamówienie i jego wiersze), ważne jest, aby zweryfikować każdy z nich i zapisać je tylko wtedy, gdy wszystkie przejdą walidację, tj. powinieneś to zrobić w transakcji.

Milan Novota
źródło
6

Myślę, że lepiej jest wysyłać oddzielne żądania w ramach jednego połączenia . Oczywiście twój serwer WWW powinien to obsługiwać

zakovyrya
źródło
5

Ostatnio się z tym zmagałem i oto nad czym pracuję.

Jeśli POST, który dodaje wiele zasobów, powiedzie się, zwróć 200 OK (rozważałem 201, ale użytkownik ostatecznie nie trafia do utworzonego zasobu) wraz ze stroną, która wyświetla wszystkie dodane zasoby, albo jako przeczytane -tylko lub edytowalna moda. Na przykład użytkownik może wybrać i POST wiele obrazów do galerii przy użyciu formularza zawierającego tylko jeden plik wejściowy. Jeśli żądanie POST zakończy się powodzeniem w całości, użytkownikowi przedstawiany jest zestaw formularzy dla każdej utworzonej reprezentacji zasobu obrazu, który pozwala mu określić więcej szczegółów na temat każdego z nich (nazwa, opis itp.).

W przypadku niepowodzenia tworzenia co najmniej jednego zasobu, program obsługi POST przerywa przetwarzanie i dołącza każdy komunikat o błędzie do tablicy. Następnie zwracany jest 419 Conflict, a użytkownik jest kierowany do strony błędu 419 Conflict, która przedstawia zawartość tablicy błędów, a także drogę powrotną do przesłanego formularza.

Eric Fuller
źródło
-2

Nie będziesz chciał wysyłać nagłówków HTTP dla 100 linii zamówienia. Nie chcesz generować więcej żądań niż to konieczne.

Wyślij całe zamówienie w jednym obiekcie JSON na serwer na adres: serwer / zamówienie lub serwer / zamówienie / nowy. Zwróć coś, co wskazuje na: server / order / order_id

Rozważ także użycie CREATE PUT zamiast POST

Wesoły
źródło
Przypuszczam, że wspomniał o metodzie HTTP POST. Nie ma czegoś takiego jak metoda CREATE HTTP.
Milan Novota
Nie ma? Och, czekaj, nie było. Zamiast tego były PUT.
Wesoły
22
Dlaczego, u licha, miałbyś używać PUT do tworzenia treści? Właśnie do tego służy metoda HTTP POST.
thecoshman
8
Używasz PUT do tworzenia zasobów, gdy chcesz, aby klient określił identyfikator URI zasobu, tak jak w webdav. Nie zgadzam się na użycie PUT przez postera, ale ma on miejsce w tworzeniu zasobów, chociaż może to być ograniczone.
user602525
2
Uwaga: POSTowanie podmiotu powinno skutkować tym, że podmiot stanie się podrzędnym wobec zasobu, którego dotyczy żądanie, i nie jest idempotentny. PUT zastępuje podmiot pod adresem i jest idempotentny. Idempotencja (słowo?) Jest ważnym oczekiwaniem dla konsumentów.
Luke Puplett,