RESTful atomic update of multiple resources?

Wyobraź sobie aplikację internetową przechowującą niektóre dane-zasoby z pewnym identyfikatorem, która przechowuje trzy załączniki (np.

Schemat URL to

data/{id}/attachment1
data/{id}/attachment2
data/{id}/attachment3

Istnieje RESTful API dla załączników dostarczających operacje GET/PUT/DELETE implementujące operacje CRUD po stronie serwera.

Pozwalając na id 123, chciałbym wykonać operację gdzie

  • załączenie1 jest zastępowane przez nowy załącznik (taki, że GET file/123/attachment1 zwraca nowy załącznik)
  • Załącznik 2 jest usunięty (tak, że GET file/123/attachment2 zwraca 404)
  • załączenie3 pozostaje bez zmian.

Aktualizacja powinna być atomowa - kompletna aktualizacja jest wykonywana przez serwer lub nic w ogóle.

Stosowanie prostych PUT file/123/attachment1 i DELETE file/123/attachment2 nie jest atomowe, ponieważ klient może się zawiesić po PUT, a serwer nie ma wskazówki, że powinien zrobić rollback w tym przypadku.

Więc jak zaimplementować operację w RESTful sposób?

Myślałem o dwóch rozwiązaniach, ale oba nie wydają się być w 100% spokojne: {]}

  • użyj patcha (można umieścić, ale PATCH lepiej odzwierciedla semantykę częściowej aktualizacji) z multipart/formularz-dane na dane / 123: The multipart/form-data jest sekwencją jednostek składających się z nowego "wniosek / pdf" związany z polem "załączenie1" oraz coś, co reprezentowałoby wartość null oznaczającą usunięcie dołączenie2.

Chociaż zapewnia to atomiczność, I wątpię, że jest to RESTful, ponieważ przeciążam metodę PATCH używając różnych list parametrów, co narusza ograniczenie uniform-interface.

  • użyj zasobu reprezentującego transakcję. Mogę zamieścić dane id 123 do transakcji-URL, który utworzyłby zasób transakcji reprezentowanie kopii bieżącego stanu przechowywanego zasobu danych na serwerze, np. transaction / data / 123. Teraz mogę zadzwonić do PUT i Usunąć na załącznikach tego zasobu tymczasowego (np. DELETE transaction/data/123/attachment2) i komunikacja commit tej wersji zasobu do serwera poprzez PUT on transakcja / dane / 123. Zapewnia to atomiczność, podczas gdy trzeba zaimplementuj dodatkową logikę po stronie serwera, aby poradzić sobie z wieloma klientami zmiana tego samego zasobu i zawieszonych klientów, którzy nigdy się nie zobowiązali.
Chociaż wydaje się to być zgodne z odpoczynkiem, wydaje się naruszać kontraint bezpaństwowości. Stan zasobu transakcyjnego to nie Stan usługi, ale stan aplikacji, ponieważ każdy zasoby transakcyjne są powiązane z jednym klientem.

Trochę tu utknąłem, więc wszelkie pomysły byłyby pomocne, dzięki!

Author: mtsz, 2012-01-29

4 answers

Chcesz użyć drugiej opcji, opcji transakcji.

Brakuje ci stworzenia transakcji:

POST /transaction

HTTP/1.1 301 Moved Permanently
Location: /transaction/1234

Teraz masz zasób transakcji, który jest obywatelem pierwszej klasy. Możesz dodać do niej, usunąć z niej, wypytywać, aby zobaczyć jego bieżącą zawartość, a następnie zatwierdzić ją lub usunąć (tj. wycofać) transakcję.

Podczas gdy transakcja jest w toku, jest to tylko kolejny zasób. Nie ma tu żadnego stanu klienta. Każdy może dodać do tego transakcja.

Kiedy wszystko się skończy, serwer stosuje wszystkie zmiany na raz za pomocą jakiegoś wewnętrznego mechanizmu transakcji, który jest poza zakresem tutaj.

Możesz przechwytywać rzeczy takie jak ETAG i nagłówki if-modified w poddziałaniach transakcja, aby gdy wszystkie zostaną zastosowane, wiesz, że coś się nie zmieniło za twoimi plecami.

 16
Author: Will Hartung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-06 14:35:37

Bardzo ciekawe pytanie. Profesor z Uniwersytetu w Lugano (Szwajcaria) napisał kilka slajdów o tej sytuacji:

Http://www.slideshare.net/cesare.pautasso/atomic-transactions-for-the-rest-of-us

Jednak nie jestem pewien, czy rozwiązanie, które dostarcza, jest całkowicie spokojne, ponieważ nie wydaje się naprawdę bezpaństwowe po stronie serwera.

Będąc szczerym, ponieważ transakcja sama w sobie składa się z wielu państw, nie sądzę, aby tam może być całkowicie spokojnym rozwiązaniem tego problemu.

 5
Author: thermz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-29 19:59:56

Zakładając, że Twoje URI są hierarchiczne:

PUT data/{id}
[attachment2,attachment3]

Częścią Twojego problemu jest to, że dołączenie 1 / 2 / 3 jest okropnym identyfikatorem. Indeks nigdy nie powinien być częścią Twojego Uri.

 0
Author: noah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-06 12:51:34

Nie mam doświadczenia, ale mam pomysł na rozwiązanie, bo właśnie ten problem mam w rozwoju.

Po pierwsze, używam analogii do mnie (klienta) wysyłającego wiadomość do Fred1 w domu (serwer z zasobami), że chcę, aby wyłączył przełącznik światła (zmiana stanu części zasobu) i włączył czajnik (zmiana stanu innej części zasobu). Po wyłączeniu włącznika światła Fred, niestety, ma atak serca.

Now I have got nothing back od Freda, żeby powiedział, czy zrobił to, o co prosiłem. Fred zostaje zastąpiony przez innego Freda. Wiadomość, którą wysłałem, nie otrzymała odpowiedzi. Jedynym sposobem, w jaki mogę kontynuować, jest spytanie Fred2, czy włącznik światła jest wyłączony, a Czajnik jest włączony (zasób jest w stanie, którego bym się spodziewał po tym, jak poprosiłem go o zrobienie rzeczy dla mnie). To jest niefortunny stan rzeczy (błąd) i dodaje do mojego obciążenia pracą, ale mogę teraz kontynuować na podstawie tego, że wiem, co Fred1 zrobił przed zawałem serca. Mogę albo wrócić do deska kreślarska (poinformuj użytkownika, że coś poszło nie tak i musimy to zrobić ponownie) lub wprowadzić zmiany, które spełnią moją prośbę, jeśli jest to nadal istotne (włącz czajnik).

To jest Początek jak bym to zrobił, są oczywiście obawy re scope, ale jeśli już zdefiniowałem swój scope (interesuje mnie tylko włącznik światła i czajnik) to powinienem mieć wystarczająco dużo informacji (znając stan włącznika światła i czajnik), aby dać nowe polecenie Fred2 bez powrót do użytkownika po instrukcję.

Jak to brzmi?

 0
Author: tentimes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-18 12:51:56