Planuję zbudować RESTfull API, ale są pewne pytania architektoniczne, które powodują pewne problemy w mojej głowie. Dodanie logiki biznesowej zaplecza do klientów to opcja, której chciałbym uniknąć, ponieważ aktualizowanie wielu platform klienckich jest trudne do utrzymania w czasie rzeczywistym, gdy logika biznesowa może się szybko zmienić.
Powiedzmy, że mamy artykuł jako zasób (api / artykuł), w jaki sposób powinniśmy wdrożyć działania takie jak publikowanie, cofanie publikacji, aktywowanie lub dezaktywowanie itd., Ale starając się, aby było to tak proste, jak to możliwe?
1) Czy powinniśmy użyć api / article / {id} / {action}, ponieważ tam może się zdarzyć wiele logiki zaplecza, np. Wypychanie do zdalnych lokalizacji lub zmiana wielu właściwości. Prawdopodobnie najtrudniejszą rzeczą jest to, że musimy odesłać wszystkie dane artykułów z powrotem do API w celu aktualizacji i nie można zaimplementować pracy wielu użytkowników. Na przykład redaktor mógłby wysłać dane o 5 sekund starsze i zastąpić poprawkę, którą jakiś inny dziennikarz właśnie zrobił 2 sekundy temu, i nie ma sposobu, bym mógł to wyjaśnić klientom, ponieważ publikujący artykuł tak naprawdę nie jest w żaden sposób związany z aktualizacją treści.
2) Utworzenie nowego zasobu może być również opcją api / article- {action} / id, ale zwrócony zasób nie byłby artykułem- {action}, ale artykułem, którego nie jestem pewien, czy jest to właściwe. Również w kodzie artykułów po stronie serwera klasa obsługuje wszystkie prace na obu zasobach i nie jestem pewien, czy jest to sprzeczne z myśleniem RESTfull
Wszelkie sugestie są mile widziane ..
api/article?action=publish
? Parametry zapytania są przeznaczone do takich przypadków, w których stan zasobu zależy od wspomnianego „algorytmu” (lub akcji). Np.api/articles?sort=asc
Jest ważnyOdpowiedzi:
Uważam, że opisane tutaj praktyki są pomocne:
źródło
/article/123/deactivations
do POST, aby utworzyć nowe żądanie dezaktywacji dla artykułu 123, serwer może nie tylko dezaktywować żądany zasób, ale faktycznie zapisać moje żądanie dezaktywacji, aby móc później odzyskać jego status.PUT /gists/:id/star
niePOST /gists/:id/star
?Operacje powodujące poważne zmiany stanu i zachowania po stronie serwera, takie jak opisana akcja „publikuj”, są trudne do jednoznacznego modelowania w REST. Rozwiązaniem, które często widzę, jest kierowanie tak złożonym zachowaniem w sposób niejawny poprzez dane.
Rozważ zamówienie towarów za pośrednictwem interfejsu API REST udostępnionego przez sprzedawcę internetowego. Zamawianie jest złożoną operacją. Kilka produktów zostanie zapakowanych i wysłanych, Twoje konto zostanie obciążone, a otrzymasz paragon. Możesz anulować swoje zamówienie na ograniczony czas, a oczywiście istnieje pełna gwarancja zwrotu pieniędzy, która pozwala odesłać produkty w celu uzyskania zwrotu pieniędzy.
Zamiast złożonej operacji zakupu taki interfejs API może umożliwić utworzenie nowego zasobu, zamówienia zakupu. Na początku możesz wprowadzić dowolne modyfikacje: dodać lub usunąć produkty, zmienić adres wysyłki, wybrać inną opcję płatności lub całkowicie anulować zamówienie. Możesz zrobić to wszystko, ponieważ jeszcze niczego nie kupiłeś, po prostu manipulujesz niektórymi danymi na serwerze.
Po złożeniu zamówienia i upływie okresu karencji serwer blokuje zamówienie, aby zapobiec dalszym zmianom. Dopiero w tym momencie rozpoczyna się złożona sekwencja operacji, ale nie można jej kontrolować bezpośrednio, tylko pośrednio, poprzez dane, które wcześniej umieściłeś w zamówieniu.
Na podstawie Twojego opisu można w ten sposób zaimplementować „publikowanie”. Zamiast ujawniać operację, umieszczasz kopię recenzowanej wersji roboczej i chcesz opublikować jako nowy zasób w obszarze / opublikuj. Gwarantuje to, że wszelkie kolejne aktualizacje wersji roboczej nie zostaną opublikowane, nawet jeśli sama operacja publikowania zakończy się kilka godzin później.
źródło
Takie rzeczy są wyzwaniem bez względu na to, co robisz. Jest to bardzo podobny problem do rozproszonej kontroli źródła (mercurial, git itp.), A rozwiązanie, zapisane w HTTP / ReST, wygląda trochę podobnie.
Załóżmy, że masz dwóch użytkowników, Alice i Boba, którzy pracują nad tym
/articles/lunch
. (dla jasności odpowiedź jest odważna)Najpierw Alice tworzy artykuł.
Serwer nie utworzył zasobu, ponieważ do żądania nie została dołączona „wersja” (zakładając identyfikator
/articles/{id}/{version}
. Aby wykonać tworzenie, Alice została przekierowana na adres URL artykułu / wersji, którą będzie tworzyć. Użytkownik Alice agent ponownie zastosuje żądanie pod nowym adresem.A teraz artykuł został utworzony. następnie Bob patrzy na artykuł:
Bob tam patrzy:
Postanawia dodać własną zmianę.
Podobnie jak w przypadku Alicji, Bob zostaje przekierowany do miejsca, w którym będzie tworzył nową wersję.
Wreszcie Alice decyduje, że chciałaby dodać do swojego artykułu:
Zamiast zostać przekierowanym jak zwykle, klient otrzymuje inny kod stanu
409
, co informuje Alice, że wersja, z której próbowała się rozgałęzić, została już rozgałęziona. Nowe zasoby i tak zostały utworzone (jak pokazano wLocation
nagłówku), a różnice między nimi uwzględniono w treści odpowiedzi. Alice wie teraz, że jej prośba musi zostać w jakiś sposób połączona.Całe to przekierowanie jest powiązane z semantyką
PUT
, która wymaga, aby nowe zasoby były tworzone dokładnie tam, gdzie prosi o to linia żądania. może to również zapisać cykl żądań za pomocąPOST
zamiast tego, ale wtedy numer wersji musiałby zostać zakodowany w żądaniu przez inną magię, co wydawało mi się mniej oczywiste dla celów ilustracji, ale prawdopodobnie nadal byłoby preferowane w prawdziwym API aby zminimalizować cykle zapytań / odpowiedzi.źródło
Oto kolejny przykład, który dotyczy nie treści dokumentów, ale bardziej stanu przejściowego. (Uważam, że przechowywanie wersji - biorąc pod uwagę, że ogólnie każda wersja może być nowym zasobem - rodzaj łatwego problemu).
Powiedzmy, że chcę udostępnić usługę działającą na komputerze za pośrednictwem usługi REST, aby można ją było zatrzymać, uruchomić, zrestartować i tak dalej.
Jakie jest najbardziej RESTful podejście tutaj? Polecenie POST / service? = Na przykład restart? Lub POST / usługa / stan z treścią, powiedzmy, „uruchomioną”?
Byłoby miło kodyfikować tutaj najlepsze praktyki i czy REST jest właściwym podejściem do tego typu sytuacji.
Po drugie, załóżmy, że chcę przeprowadzić akcję z usługi, która nie wpływa na jej stan, ale raczej wywołuje efekt uboczny. Na przykład usługa pocztowa, która wysyła raport, zbudowany w czasie połączenia, na kilka adresów e-mail.
GET / report może być sposobem na samodzielne uzyskanie kopii raportu; ale co, jeśli chcemy przesunąć na stronę serwera dalsze działania, takie jak e-mail, jak mówię powyżej. Lub pisanie do bazy danych.
Te przypadki tańczą wokół podziału na zasoby i widzę sposoby radzenia sobie z nimi w sposób zorientowany na REST, ale szczerze mówiąc, wydaje się to trochę hackowaniem. Być może kluczowym pytaniem jest, czy interfejs API REST powinien ogólnie wspierać działania niepożądane.
źródło
REST jest zorientowany na dane i jako takie zasoby działają najlepiej jako „rzeczy”, a nie akcje. Ukryta semantyka metod http; GET, PUT, DELETE itp. Służą do wzmocnienia orientacji. POST jest oczywiście wyjątkiem.
Zasób może być mieszanką danych, tj. treść artykułu; i metadane tj. opublikowane, zablokowane, wersja. Istnieje wiele innych możliwych sposobów podzielenia danych, ale musisz najpierw sprawdzić, jak będzie wyglądał przepływ danych, aby określić najbardziej optymalny (jeśli taki istnieje). Na przykład może być tak, że poprawki powinny być ich własnym zasobem w ramach artykułu, jak sugeruje TokenMacGuy.
Jeśli chodzi o implementację, prawdopodobnie zrobiłbym coś takiego, co sugeruje TockenMacGuy. Dodałbym również pola metadanych w artykule, a nie w wersji, takie jak „zablokowane” i „opublikowane”.
źródło
Nie myśl o tym jako o bezpośredniej manipulacji stanem artykułu. Zamiast tego składasz zlecenie zmiany z prośbą o utworzenie artykułu.
Można modelować umieszczanie w kolejności zmian jako tworzenie nowego zasobu zlecenia zmiany (POST). Istnieje wiele zalet. Na przykład możesz określić przyszłą datę i godzinę, kiedy artykuł powinien zostać opublikowany w ramach zlecenia zmiany, i pozwolić serwerowi martwić się o to, jak to zostanie zaimplementowane.
Jeśli publikowanie nie jest procesem natychmiastowym, nie musisz czekać, aż zakończy się, zanim wrócisz do klienta. Wystarczy potwierdzić, że zlecenie zmiany zostało utworzone i zwrócić identyfikator zlecenia zmiany. Następnie możesz użyć adresu URL odpowiadającego temu zleceniu zmiany, aby udostępnić status zlecenia zmiany.
Kluczowym wglądem było dla mnie rozpoznanie tej metafory kolejności zmian, która jest po prostu innym sposobem opisania programowania obiektowego. Zamiast zasobów nazywamy obiekty. Zamiast zleceń zmiany nazywamy je wiadomościami. Jednym ze sposobów wysłania wiadomości z A do B w OO jest wywołanie metody A na B. Innym sposobem na zrobienie tego, szczególnie gdy A i B są na różnych komputerach, jest utworzenie przez A nowego obiektu, M i wyślij go do B. REST po prostu formalizuje ten proces.
źródło
Jeśli dobrze cię rozumiem, uważam, że to, co masz, jest bardziej kwestią ustalenia „reguł biznesowych” niż kwestią techniczną.
Fakt, że artykuł może zostać zastąpiony, można rozwiązać, wprowadzając poziomy autoryzacji, w których starsi użytkownicy mogą zastępować wersje młodszych użytkowników, a także wprowadzając wersje, a także kolumnę do przechwytywania stanu artykułu (np. „W fazie rozwoju”, „końcowy” itp.), możesz to przezwyciężyć. Możesz także dać użytkownikowi możliwość wybrania danej wersji albo przez kombinację czasu przesłania i numeru wersji.
We wszystkich powyższych przypadkach usługa musi implementować ustawione reguły biznesowe. Możesz więc wywołać usługę z parametrami: identyfikator użytkownika, artykuł, wersja, działanie (w przypadku gdy wersja jest opcjonalna, znowu zależy to od reguł biznesowych).
źródło