Reprezentuj działania (czasowniki) w URI REST

16

Mam operację drukowania do wykonania dla dokumentów klienta. Potrzebuję też innych standardowych operacji, takich jak dodawanie, aktualizowanie, usuwanie. więc mam następujące:

  • Do tworzenia nowego klienta:
    URI = / customer / {id}, wpisz = POST, Methodname = CreateCustomer ()
  • Do aktualizacji:
    URI: / customer / {id}, wpisz = PUT, method = UpdateCstomer ()
  • W przypadku opcji Usuń klienta:
    URI = / customer / {id}, wpisz = DELETE, Methodname = DeleteCustomer ()
  • W przypadku widoku:
    URI: / customer / {id}, wpisz = GET, method = GetCustomer ()

Teraz, jeśli muszę wydrukować dokument dla tego klienta, potrzebuję funkcji drukowania. Mój identyfikator URI może wyglądać następująco: / customer / {id}, type = POST, method = PrintCustomer (). Ale użyłem tego typu URI i POST dla CreateCustomer. Chciałem, aby identyfikator URI wyglądał tak: / customer / Print / {id}, type = POST, method = PrintCustomer ().

Ale nie mogę mieć czasownika „Drukuj” w moim URI. Jak najlepiej to zrobić? Myślałem o / customer / document / {id} jako URI ... ale napotkam ten sam problem. Chciałbym mieć operacje CRUD na „dokumencie”. Więc znów zabrakło mi tego, co chciałbym użyć do „drukowania”. Proszę doradź.

Nitya Maha
źródło
2
Drukowanie jest zwykle operacją po stronie klienta, więc jestem ciekawy - w jaki sposób konfiguracja wymaga od Ciebie wysłania polecenia do serwera REST?
Shauna
2
@Shauna Niekoniecznie URI może być prośbą do serwera o wersję zasobu do wydruku (tj. Inny widok).
Evan Plaice
1
@EvanPlaice - Fair enough, chociaż nadal pozostawia akt drukowania do klienta (które nawet po pobraniu do druku w obsłudze wersji po stronie serwera, a następnie zdecydować, co będzie urządzenie do drukowania i wysyłania samego polecenia drukowania, nawet jeśli polecenie przechodzi do serwera wydruku). Prośba o uzyskanie wersji zasobu przeznaczonej do wydruku byłaby logicznie ... no cóż ... GET.
Shauna
@Shauna Wyzwalanie zadania drukowania z samego żądania HTTP byłoby niemożliwe ze względu na bezpieczeństwo przeglądarki. Prośba o wersję do wydruku jest tylko żądaniem GET, ale nadal potrzebujesz sposobu, aby określić, że przeglądarka powinna renderować wersję do wydruku. Możesz podać inny adres URL, ale byłoby to sprzeczne z zasadami REST, ponieważ tak naprawdę nie żądasz innego zasobu, tylko inną transformację tego samego zasobu. Stąd powód określenia transformacji za pomocą parametru zapytania i / lub typu zawartości.
Evan Plaice,
Nie mam wystarczającej liczby przedstawicieli do opublikowania jako odpowiedzi, ale uważam za interesujące, że tyk.io/rest-never-crud twierdzi, że POST /customers/123/printjest to słuszna rzecz.
jlh

Odpowiedzi:

9

POSTnie oznacza „stwórz”, oznacza „proces”. Możesz utworzyć nowy zasób, wysyłając odpowiednie żądanie do istniejącego zasobu (tj. Opublikuj, /customersaby utworzyć nowego klienta). Ale możesz także użyć POSTdo wypełnienia wszystkich innych akcji, które nie odpowiadają zgrabnemu paradygmatowi CRUD.

W przypadku drukowania powinieneś rozważyć akt drukowania jako sam zasób. Poprosisz system o utworzenie dla ciebie „zadania drukowania”. Oznacza to, że możesz mieć prints/zasób, który będzie pojemnikiem dla wszystkich żądanych wydruków. Jeśli chcesz wydrukować coś, POSTto dokument do tego zasobu, który zawiera wszystkie informacje o wydruku, który chcesz utworzyć, identyfikując zasoby, które chcesz wydrukować, z linkami do nich.

Jako dokument JSON mógłby wyglądać następująco:

{
   contents: ["http://site/customers/12345"],
   paper-size: "A4",
   duplex: "true"
}

Oczywiście musisz to dostosować, aby odpowiadało temu, co chcesz zrobić. Najważniejsze jest to, że identyfikujesz inne zasoby do wydrukowania, podając ich adres URL.

W odpowiedzi na prośbę możesz po prostu zwrócić a 200 OKlub a 204 No-Contenti potraktować to jako proces typu „odpal i zapomnij”. Jeśli jednak chcesz go ulepszyć, możesz zwrócić 201 Createdi podać adres URL nowo utworzonego zadania drukowania, np /prints/12345.

Użytkownik może następnie wykonać GETna zasobie, aby zobaczyć status swojego zadania drukowania (w toku, w toku, itp.), Lub może poprosić o anulowanie zadania przez wydanie polecenia DELETE.

Po ponownym sformułowaniu problemu dotyczącego zasobów projekt RESTful powinien pojawić się w sposób naturalny i dać możliwość rozszerzenia i ulepszenia w sposób, który mógł nie być od razu rozważany.

Paul Turner
źródło
2
POST zwykle oznacza tworzenie / wstawianie, podczas gdy put zwykle oznacza aktualizację zapisz / aktualizuj. Tak to jest zdefiniowane w REST, nawet jeśli nie jest tak, jak zwykle jest używane w HTML.
Evan Plaice,
2
@EvanPlaice nazwy specyfikacji HTTP PUT jako czasownik tworzenia / aktualizacji (używa modelu tworzenia + aktualizacji zamiast bardziej znanego czasu tworzenia + pobierania + aktualizacji), a POST jest czasownikiem „przetwarzania danych”, a także czasownikiem „dołączania” . Roy Fielding na swoim blogu opisał POST jako czasownik do użycia, gdy nie chcesz standaryzować operacji. POST przyjmuje semantykę „tworzenia”, jeśli weźmie się pod uwagę dołączenie nowego elementu do kolekcji elementów. W takim przypadku Tragedian uderzył paznokciem w głowę za pomocą testu POST w celu przetworzenia lub dodania zadania drukowania.
Rob
@RobY OK, to ma sens. Jako przykład można użyć PUT do reprezentowania SPROC zaprojektowanego do wprowadzania danych do bazy danych. Natomiast test POST może składać się z etapów pośrednich i mutacji wymaganych do zebrania / przygotowania tych danych. Projekt operacji POST może ulec zmianie lub zostać zastąpiony w miarę ewolucji projektu, ale operacje PUT reprezentują model, który (najlepiej) nie powinien ulec zmianie. Zaktualizowałbym moją odpowiedź, ale ta już świetnie sobie radzi z wyjaśnianiem różnicy.
Evan Plaice,
4

Zrobiłem to wcześniej. Aby wydrukować dokument, zwracam tylko wersję pdf zasobu. Klient musi jedynie wysłać żądanie GET dla zasobu za pomocą polecenia Akceptuj nagłówek / pdf.

Pozwala to również uniknąć tworzenia nowego identyfikatora URI dla zasobu tymczasowego, takiego jak zadanie drukowania. Użycie nagłówka HTTP jest również częścią REST i utrzymuje URI w czystości.

imel96
źródło
3

Wystarczy dodać parametr do GET bieżącego identyfikatora URI

Używanie identyfikatora URI do wielu działań jest dość typowe.

Jeśli mówisz o tym samym zasobie, ale o innej akcji, zdefiniuj go jako parametr.

/ customer / {id}? print = true

Następnie, gdy definiujesz metodę GET, wykrywasz obecność parametru drukowania i postępujesz inaczej.

REST jest definiowany w następujący sposób:

  • POST - Utwórz rekord, zasób lub zasób
  • PUT - aktualizacja, rekord, zasób lub zasób
  • USUŃ - Usuń rekord, zasób lub zasób

Z drugiej strony GET ma być wykorzystywany na wiele sposobów, ponieważ zwykle istnieje wiele różnych form, z których można odzyskać zasób. Dlatego też żądania GET są reprezentowane jako ciąg zapytania. Jeśli pracujesz z zasobem bazy danych, dosłownie pobierasz widok za pomocą zapytania, ale REST jest celowo wyodrębniany na wyższy poziom, ponieważ jest zaprojektowany do obsługi wielu różnych rodzajów zasobów.

Specyfikacja REST jest dość przyszłościowa, mimo że interfejsy API dopiero niedawno zaczęły ją intensywnie wykorzystywać.

Jeśli chcesz dowiedzieć się więcej o protokołach REST, gorąco polecam przeczytanie „ Haters Gonna Hate HATEOAS ”.


Aktualizacja:

@Shauna wskazał na interesującą dziurę w moim rozumowaniu. Nie ma prawdziwe właściwa droga i wiele form są uważane za dopuszczalne. Zastanowiłem się nad tym trochę, a ponieważ zamierzonym zastosowaniem jest przekształcenie danych w inną reprezentację, sensowne jest zdefiniowanie transformacji jako nowego typu MIME.

Na przykład możesz reprezentować identyfikator URI jako:

/customer/{id}+print

Gdzie można ustawić odpowiedź Content-Type na text / html + print. W ten sposób będziesz mieć również możliwość zdefiniowania większej liczby transformacji w przyszłości.

Na przykład:

// for application/json
/customer/{id}+json

// for application/atom+xml
/customer/{id}+atom

Tak czy inaczej, wszystkie formy są dopuszczalne. Implementacja, którą wybierzesz, zależy bardziej od osobistych preferencji i możliwości twojego serwera.

Na bok: Pozwól, że wyjaśnię, ponieważ wydaje się, że istnieje pewne zamieszanie. Parametr zapytania „print” i / lub typ zawartości służy do określenia sposobu transformacji zasobu. Nie jak uruchomić fizyczne zadanie drukowania. Ze względów bezpieczeństwa dostęp na poziomie sprzętowym jest zawsze pozostawiony użytkownikowi / klientowi / przeglądarce.

Evan Plaice
źródło
Aby dodać - jako alternatywę dla ciągu zapytania ( ?print=true), możesz także użyć parametrów URI (tj. - /customer/{id}/printable). To, którego użyjesz, będzie w dużej mierze zależeć od standardu systemu (CMS, framework, kod ogólnie) skonfigurowanego do obsługi. Oba są uważane za ważne i dopuszczalne .
Shauna
@Shauna Technicznie najlepszym rozwiązaniem byłoby zastosowanie typu MIME specyficznego dla drukowania z identyfikatorem URI „/ customer / {id} + print” i odpowiedzią typu MIME text / html + print. Zaletą takiego podejścia jest możliwość tworzenia transformacji dla wielu typów MIME (np. Text / html, text / x-markdown, application / json itp.) Dla tego samego identyfikatora URI. Wadą prezentowanego rozwiązania jest konieczność utworzenia dodatkowego identyfikatora URI (i zdefiniowania innej trasy) dla każdego innego typu MIME. W pewnym sensie jest to sprzeczne z celem używania REST.
Evan Plaice
(ciąg dalszy) Argumentowałbym, że hacki URI są anty-wzorcem wprowadzonym przede wszystkim przez społeczność ROR, ale to nie znaczy, że nie są przydatne. Wraz z pojawieniem się lepszych niskopoziomowych serwerów HTTPd staje się coraz łatwiejsze do wdrożenia REST w sposób, który w pełni wykorzystuje jego potencjał. Od czasów, gdy jedyną opcją było Apache i przekierowywanie wszystkiego przez index.html.
Evan Plaice
2
GET nie powinien zmieniać stanu ani wywoływać skutków ubocznych. Weź pod uwagę, że GET jest idempotentny, co oznacza, że ​​oprogramowanie pośrednie może ponowić żądanie, jeśli go nie przejdzie. W takim przypadku każda ponowna próba spowoduje powstanie nowej, świeżo wydrukowanej kopii dokumentu. ;)
Rob
@RobY Zakładałem, że akcja „print” nie będzie obsługiwać procesu fizycznego drukowania dokumentu, ponieważ lepiej by to zrobiła przeglądarka i sterownik drukarki. Dane wyjściowe z nośnika / wydruku zwracałyby raczej „przyjazną do wydruku” reprezentację dokumentu. Dlatego utrzymywana jest idempotencja. Dobrze, że wysyłanie zadań drukowania przez Internet w sposób bezpaństwowy byłoby złym czasem.
Evan Plaice,