2013-04-18 5 views
11

Próbuję zaprojektować RESTful web API dla naszej usługi przy użyciu interfejsu ASP.NET Web API. Mam kłopot z ustaleniem, w jaki sposób przekierować działania inne niż CRUD do właściwego działania kontrolera. Załóżmy, że mój zasób to drzwi. Mogę zrobić wszystkie znane rzeczy z CRUD przy moich drzwiach. Powiedzmy, że wzorem dla moich drzwiach jest:Jak trasować akcje inne niż CRUD w RESTful ASP.NET Web API?

public class Door 
{ 
    public long Id { get; set; } 
    public string InsideRoomName { get; set; } 
    public string OutsideRoomName { get; set; } 
} 

mogę zrobić wszystko z moich standardowych operacji CRUD za pośrednictwem mojego Web API:

POST: http://api.contoso.com/v1/doors 
GET: http://api.contoso.com/v1/doors 
GET: http://api.contoso.com/v1/doors/1234 
GET: http://api.contoso.com/v1/doors?InsideRoomName=Cafeteria 
PUT: http://api.contoso.com/v1/doors/1234 
DELETE: http://api.contoso.com/v1/doors/1234 

i tak dalej. Tam, gdzie napotykam na kłopoty, kiedy muszę wymodelować działania non-CRUD przeciwko moim drzwiom. Chcę modelować czasowniki blokujące i odblokowujące dla mojego zasobu. W przypadku czytania za pomocą instrukcji wydaje się, że podczas korzystania z działań niestandardowych przełącza się na wywołanie stylu RPC. To daje mi ścieżkę:

PUT: http://api.contoso.com/v1/doors/1234/lock 
PUT: http://api.contoso.com/v1/doors/1234/unlock 

Wydaje się to sprzeczne z duchem REST która ma na celu wskazać ścieżkę do zasobu. Przypuszczam, że mógłbym model czasownik jako zasób:

POST: http://api.contoso.com/v1/doors/1234/lockrequests 
POST: http://api.contoso.com/v1/doors/1234/unlockrequests 

w tym przypadku nadal może wykorzystać polecić {kontroler}/{id}/{akcja}, ale wydaje się, że nadal jestem tworząc mieszaną RPC/REST API. Czy jest możliwe, a nawet zalecane, jeśli chodzi o interfejsy REST, aby umieścić niestandardową akcję na liście parametrów?

PUT: http://api.contoso.com/v1/doors/1234?lock 
PUT: http://api.contoso.com/v1/doors/1234?unlock 

mogłem przewidzieć potrzebę posiadania tego połączenia obsługiwanego z parametrami zapytania, jak również, takich jak:

PUT: http://api.contoso.com/v1/doors?lock&InsideRoomName=Cafeteria 

Jak utworzyć trasę na mapie Ten wniosek do mojego DoorsController?

public class DoorsController : ApiController 
{ 
    public IEnumerable<Doord> Get(); 
    public Door Get(long id); 
    public void Put(long id, Door door); 
    public void Post(Door door); 
    public void Delete(long id); 

    public void Lock(long id); 
    public void Unlock(long id); 
    public void Lock(string InsideRoomName); 
} 

I może być wprowadzenie pewnych błędnych założeń dotyczących tego, co tu jest i nie jest najlepsze praktyki w odniesieniu do projektowania REST API, więc wszelkie wskazówki tam jest doceniana również.

+0

Google korzysta z interfejsu REST API z Bloggerem, a korzysta z działań w trybie REST! https://developers.google.com/blogger/docs/3.0/reference/posts/publish – padibro

Odpowiedz

6

obsłużyć scenariusz lock/unlock można rozważyć dodanie właściwość State do obiektu Door:

public State State { get; set; } 

gdzie państwo jest enum z dostępnych wartości, na przykład

{ 
LockedFromOutsideRoom, 
LockedFromInsideRoom, 
Open 
} 

Dla wyjaśnienia: Że jesteś dodając do stanu obiektu nie jest wbrew zasadom spokojny jak państwo jest przeszło api za każdym razem wykonać połączenie, aby zrobić coś z drzwiami.

Następnie za pomocą api wyślesz żądanie PUT/POST, aby zmienić stan Drzwi na każdym zamku/odblokowaniu. Post będzie prawdopodobnie lepiej, jak to tylko jedną właściwość, która zostanie zaktualizowany:

POST: http://api.contoso.com/v1/doors/1234/state 
body: {"State":"LockedFromInsideRoom"} 
+0

Czy muszę zarejestrować niestandardową trasę do tego w WebApiConfig.cs? Wydaje się, że to nadal jest zgodne ze schematem {kontroler}/{id}/{action}, w którym mój kontroler będzie teraz miał DoorsController :: State (długi id, stan state); metoda. – JadeMason

+1

@JadeMason - ta trasa powinna działać dobrze –

+0

@JoannaTurban miła odpowiedź! Zrobiłbym to nawet z PUT, tak jak poniżej: 'PUT: http://api.contoso.com/v1/doors/1234 body: {" State ":" LockedFromInsideRoom "}' ponieważ, faktycznie * aktualizujesz * stan. Ponadto czuje się bardziej spójny z PUT [idempotence] (http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods_and_web_applications): Za każdym razem, gdy ten URL jest wymagany, daje dokładnie taki sam wynik. –

1

Z perspektywy REST prawdopodobnie chcesz być leczeniu blokadę jako zasób sam w sobie. W ten sposób tworzysz i usuniesz blokadę niezależnie od drzwi (chociaż prawdopodobnie zlokalizuj punkt końcowy blokady od reprezentacji drzwi).Adres URL zasobu prawdopodobnie będzie powiązany z adresem URL drzwi, jednak z perspektywy RESTful nie ma to znaczenia. REST dotyczy relacji między zasobami, więc ważną częścią jest to, że URL blokady można odczytać z reprezentacji drzwi.

2

Z zasady firmy RESTful być może najlepiej jest wprowadzić właściwość "status", aby zarządzać działaniami innymi niż CURD. Ale nie sądzę, że odpowiada prawdziwemu rozwojowi produkcji.

Każda odpowiedź na tego typu pytanie wygląda na to, że musisz wymusić obejście, aby wymusić zgodność interfejsu API z RESTful. Ale moim zmartwieniem jest to, czy to naprawdę czyni wygodę zarówno dla użytkownika, jak i programisty?

Rzućmy okiem na projekt blogera Google API3.0: https://developers.google.com/blogger/docs/3.0/reference, używa on adresu URL dla działań innych niż CURD.

A to ciekawe,

POST /blogs/blogId/posts/postId/comments/commentId/spam 

a opis jest

Zaznacza komentarz jako spam. Spowoduje to ustawienie statusu komentarza na spam i ukrycie go w domyślnym renderowaniu komentarzy.

Możesz zobaczyć, że komentarz ma status wskazujący, czy jest to spam, czy nie, ale nie został zaprojektowany tak, jak wspomniana wyżej przez JoannaTurban.

Myślę, że z punktu widzenia użytkownika jest to wygodniejsze. Nie trzeba przejmować się strukturą i wartością wyliczeniową "statusu". I rzeczywiście możesz umieścić wiele atrybutów w definicji "statusu", takich jak "isItSpam", "isItReplied", "isItPublic" itd. Projekt stanie się nieprzyjazny, jeśli status ma wiele rzeczy.

W przypadku niektórych wymagań logiki biznesowej, aby użyć łatwego do zrozumienia czasownika, zamiast próbować uczynić go całkowicie "prawdziwym" REST, jest bardziej wydajny, zarówno dla użytkownika, jak i programisty. To jest moja opinia.