REST API-masowe tworzenie lub aktualizowanie w jednym żądaniu

Załóżmy, że istnieją dwa zasoby Binder i Doc z relacją asocjacyjną, co oznacza, że Doc i Binder stoją same. Doc może lub nie należy do Binder i Binder może być pusty.

Jeśli chcę zaprojektować REST API, które pozwala użytkownikowi wysłać kolekcję Doc s, W Jednym żądaniu , jak poniżej:

{
  "docs": [
    {"doc_number": 1, "binder": 1}, 
    {"doc_number": 5, "binder": 8},
    {"doc_number": 6, "binder": 3}
  ]
}

I dla każdego doc w docs,

  • Jeśli doc istnieje to przypisz go do Binder
  • Jeśli doc nie istnieje, utwórz go, a następnie przypisz

Jestem naprawdę zdezorientowany co do tego, jak to powinno być realizowane.

  • jakiej metody HTTP użyć?
  • jaki kod odpowiedzi należy zwrócić?
  • czy to w ogóle kwalifikuje się do odpoczynku?
  • jak wyglądałby URI? /binders/docs?
  • Obsługa żądania zbiorczego, co jeśli kilka elementów spowoduje błąd, ale drugi przejdzie. Jaki kod odpowiedzi należy zwrócić? Czy operacja masowa powinna być atomowe?
Author: norbertpy, 2015-02-19

4 answers

Myślę, że możesz użyć metody POST lub PATCH, aby poradzić sobie z tym, ponieważ zazwyczaj projektują do tego.

  • Użycie metody POST jest zwykle używane do dodania elementu, gdy jest używany w zasobie listy, ale można również obsługiwać kilka akcji dla tej metody. Zobacz tę odpowiedź: Jak zaktualizować zbiór zasobów REST . Można również obsługiwać różne formaty reprezentacji dla danych wejściowych (jeśli odpowiadają one tablicy lub pojedynczemu elementów).

    W takim przypadku nie jest konieczne definiowanie formatu, aby opisać aktualizację.

  • Użycie metody PATCH jest również odpowiednie, ponieważ odpowiednie żądania odpowiadają częściowej aktualizacji. Zgodnie z RFC5789 (http://tools.ietf.org/html/rfc5789):

    Kilka aplikacji rozszerzających protokół HTTP (Hypertext Transfer Protocol) wymaga funkcji do częściowej modyfikacji zasobów. Istniejąca metoda HTTP PUT pozwala tylko całkowite zastąpienie dokumentu. Ta propozycja dodaje nową metodę HTTP, PATCH, aby zmodyfikować istniejący zasób HTTP.

    W tym przypadku musisz zdefiniować swój format, aby opisać częściową aktualizację.

Myślę, że w tym przypadku POST i PATCH są dość podobne, ponieważ nie trzeba opisywać operacji do wykonania dla każdego elementu. Powiedziałbym, że to zależy od formatu reprezentacji do wysłania.

Przypadek PUT jest nieco mniej czysto. W rzeczywistości, używając metody PUT, Należy podać całą listę. W rzeczywistości przedstawiona reprezentacja we wniosku zostanie zastąpiona zasobem listy nr 1.

Możesz mieć dwie opcje dotyczące ścieżek zasobów.

  • korzystanie ze ścieżki zasobów dla listy doc

W tym przypadku należy wyraźnie podać link do dokumentów z binderem w reprezentacji, którą podajesz w zapytaniu.

Oto przykładowa trasa dla tego /docs.

Treść takiego podejścia może być dla metody POST:

[
    { "doc_number": 1, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 2, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 3, "binder": 5, (other fields in the case of creation) },
    (...)
]
  • użycie ścieżki podrzędnej elementu bindującego

Ponadto można również rozważyć wykorzystanie tras podrzędnych do opisania powiązania między dokumentami i binderami. Wskazówki dotyczące powiązania między dokumentem a programem bindującym nie muszą być teraz określone w treści żądania.

Oto przykładowa trasa tego /binder/{binderId}/docs. W tym przypadku, wysłanie listy dokumentów za pomocą metody POST lub PATCH spowoduje dołączenie dokumentów do segregatora o identyfikatorze binderId Po utworzeniu dokumentu, jeśli nie istnieje.

Treść takiego podejścia może być dla metody POST:

[
    { "doc_number": 1, (other fields in the case of creation) },
    { "doc_number": 2, (other fields in the case of creation) },
    { "doc_number": 3, (other fields in the case of creation) },
    (...)
]

Jeśli chodzi o odpowiedź, to do ciebie należy określenie poziomu odpowiedzi i błędów, które należy zwrócić. Widzę dwa poziomy: poziom statusu (globalny) i poziom ładunku (cieńszy). To również do ciebie, aby określić, czy wszystkie wstawki / aktualizacje odpowiadający twojej prośbie musi być atomic lub nie.

  • Atomic

W tym przypadku możesz wykorzystać status HTTP. Jeśli wszystko pójdzie dobrze, otrzymasz status 200. Jeśli nie, inny status jak 400 jeśli podane dane nie są poprawne (na przykład binder id nie jest poprawny) lub coś innego.

  • Non atomic

W tym przypadku zostanie zwrócony status 200 i to do reprezentacji odpowiedzi należy opisanie co zostało zrobione i gdzie w końcu pojawiają się błędy. ElasticSearch ma punkt końcowy w REST API dla zbiorczej aktualizacji. To może dać ci kilka pomysłów na tym poziomie: http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html .

  • asynchroniczne

Można również zaimplementować asynchroniczne przetwarzanie do obsługi dostarczonych danych. W takim przypadku zwracany jest status HTTP 202. Klient musi wyciągnąć dodatkowy zasób, aby zobaczyć co się stanie.

Przed zakończeniem chciałbym również zauważyć, że specyfikacja OData rozwiązuje problem dotyczący relacji między obiektami o nazwie linki nawigacyjne . Może mógłbyś na to spojrzeć; -)

Poniższy link może Ci również pomóc: https://templth.wordpress.com/2014/12/15/designing-a-web-api/.

Hope it helps you, Thierry

 35
Author: Thierry Templier,
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
2017-05-23 12:34:47

Prawdopodobnie będziesz musiał użyć POST lub patcha, ponieważ jest mało prawdopodobne, aby pojedyncze żądanie aktualizujące i tworzące wiele zasobów było idempotentne.

Robienie {[0] } jest zdecydowanie ważną opcją. Korzystanie ze standardowych formatów łat może być trudne dla danego scenariusza. Nie jestem pewien.

Przydałoby ci się 200. Możesz również użyć 207-Multi Status Można to zrobić w sposób spokojny. Kluczem moim zdaniem jest posiadanie jakiegoś zasobu który jest przeznaczony do przyjęcia zestawu dokumentów do aktualizacji/utworzenia.

Jeśli użyjesz metody PATCH, myślę, że Twoja operacja powinna być atomowa. tzn. nie użyłbym kodu statusu 207, a następnie zgłaszał sukcesy i porażki w ciele odpowiedzi. Jeśli użyjesz operacji POST, podejście 207 jest wykonalne. Będziesz musiał zaprojektować własne ciało odpowiedzi, aby komunikować się, które operacje zakończyły się sukcesem, a które nie. Nie znam znormalizowanego.

 25
Author: Darrel Miller,
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
2015-02-24 20:22:59

PUT ing

PUT /binders/{id}/docs Tworzenie i aktualizowanie pojedynczego dokumentu do segregatora

Np.:

PUT /binders/1/docs HTTP/1.1
{
  "docNumber" : 1
}

PATCH ing

PATCH /docs twórz dokumenty, jeśli nie istnieją i odnoś je do segregatorów

Np.:

PATCH /docs HTTP/1.1
[
    { "op" : "add", "path" : "/binder/1/docs", "value" : { "doc_number" : 1 } },
    { "op" : "add", "path" : "/binder/8/docs", "value" : { "doc_number" : 8 } },
    { "op" : "add", "path" : "/binder/3/docs", "value" : { "doc_number" : 6 } }
] 

Dołączę dodatkowe spostrzeżenia później, ale w międzyczasie, jeśli chcesz, zajrzyj do RFC 5789, RFC 6902 i Williama Duranda Proszę. Nie Łataj jak idiota wpis na blogu.

 11
Author: Mauricio Morales,
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
2015-02-22 14:50:22

W projekcie, w którym pracowałem, rozwiązaliśmy ten problem poprzez zaimplementowanie czegoś, co nazwaliśmy 'Batch' requests. Zdefiniowaliśmy ścieżkę /batch gdzie zaakceptowaliśmy json w następującym formacie:

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 5,
         binder: 8
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      }
   },
]

Odpowiedź ma kod statusu 207 (Multi-Status) i wygląda tak:

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
      status: 200
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         error: {
            msg: 'A document with doc_number 5 already exists'
            ...
         }
      },
      status: 409
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      },
      status: 200
   },
]

Możesz również dodać obsługę nagłówków w tej strukturze. Zaimplementowaliśmy coś, co okazało się przydatne, czyli zmienne używane między żądaniami w partii, co oznacza, że możemy użyć odpowiedzi z jednego żądania jako wejście do innego.

Facebook i Google mają podobne implementations:
https://developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests

Jeśli chcesz utworzyć lub zaktualizować zasób z tym samym wywołaniem, użyję POST lub PUT w zależności od przypadku. Jeśli dokument już istnieje, czy chcesz, aby cały dokument był:

  1. zastąpione dokumentem, który wysyłasz (tj. brakujące właściwości w żądaniu zostaną usunięte i już istniejące nadpisane)?
  2. połączone z przesłanym dokumentem (tzn. brakujące właściwości w żądaniu nie zostaną usunięte, a istniejące właściwości zostaną nadpisane)?

W przypadku, gdy chcesz zachowania z alternatywy 1, powinieneś użyć posta, a w przypadku, gdy chcesz zachowania z alternatywy 2, powinieneś użyć PUT.

Http://restcookbook.com/HTTP%20Methods/put-vs-post/

Jako ludzie już zasugerowałem, że możesz również przejść do patcha, ale wolę zachować proste API i nie używać dodatkowych czasowników, jeśli nie są potrzebne.
 4
Author: David Berg,
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
2017-06-12 13:19:51