I have a controller Groups
with the following actions:
public GroupModel Get(int ID)
public GroupModel Post(CreateGroupModel model)
public void Put(PublicUpdateGroupModel model)
public void PutAddContacts(UpdateContactsModel model)
public void PutRemoveContacts(UpdateContactsModel model)
public void Delete(int ID)
And what I would like to do is use standard REST routing to call the standard get, post, put, delete mehods. But call the PutAddContacts
and PutRemoveContacts
if the action names are appended to the url, for example:
GET groups/ - calls Get method
POST groups/ - calls Post method
PUT groups/ - calls Put method
DELETE groups/ - calls Delete method
PUT groups/addcontacts - calls PutAddContacts method
PUT groups/removecontacts - calls PutRemoveContacts method
Is it possible to set up routing to do this or do I need to go down the RPC route for routing if I want to use action names in my URL's?
As mentioned, Web API controller can include multiple Get methods with different parameters and types. Let's add following action methods in StudentController to demonstrate how Web API handles multiple HTTP GET requests.
Usually a Web API controller has maximum of five actions - Get(), Get(id), Post(), Put(), and Delete().
What you have now
To utilize your methods as above you'll need to go RPC. That is because your example is already half way steeped in the RPC style of doing things. Default WebAPI routes encourage RESTful setups, but if you made a minor alteration to your routes everything would start working. For example you could change your default route to something like a typical MVC route:
routes.MapRoute( name : "Default",
url : "{controller}/{action}/{id}",
defaults: new { controller = "Home",
action = "Index",
id = UrlParameter.Optional });
After adding the route, call things in typical MVC fashion where you use the controller name & action. From your question, however, I suspect you actually want to be RESTful, instead of just getting it to work so read on...
Being RESTful
REST doesn't require HTTP, although the two are often discussed together. REST is really about every resource having a semantically accurate representation. When using HTTP that means unique URI's that respect HTTP semantics. So for example, a call using HTTP GET should never modify data because that violates HTTP's definition of GET and confused HTTP infrastructure like caches.
POST/PUT vs MERGE/PATCH
We're all familiar with GET, POST, PUT, HEAD, etc.. as HTTP methods. Generally, GET is for retrieving, POST is for adding, and PUT is for modifying (although subject to lots of debate). Yet, you have two types of modifications: adding items and removing items from a collection. So are those both PUT or something else? The community hasn't quite settled on how to do this.
Option 1: Custom media type - The HTTP spec really allows for all sorts methods, it's the browsers that really restrict us to the familiar subset. So you can create MERGE (a Roy Fielding work around) or PATCH (an oData work around) methods and define the behavior for this new media type -- maybe one for adding and one for removing.
Option 2: Use POST/PUT - Use PUT for both adding and removing contacts. Just treat the list of ID's like a toggle (if exists remove, if missing add) or alternatley include enough information to know what to do. Then return an HTTP 303 indicating to the client it has a stale state and refresh.
Option 3: Full List - If your set is a reasonable size, you can always pass a complete list of contacts every time you want to update. This way the logic is a super simple wipe and replace.
What really matters from a RESTful perspective is that your application behaves in a consistent way across all methods. So if MERGE means add, it should always mean add. If you expect a complete set of ID's passed to PUT then always pass a complete set.
Controller Design
If it were me, I would break your controller into multiple controllers. One controller deals with Groups another deals Contacts (as a group) and a third deals with one contact within a group. Something like ...
//api/Group/
public List<GroupModel> Get()
public GroupModel Get(int ID)
public GroupModel Post(GroupModel model) //add a group
public GroupModel Put(GroupModel model) //update a group (see comments above)
public void Delete(int ID)
//api/GroupContacts/
public ContactsModel Get() //gets complete list
public void PostContacts(ContactsModel model) //pushes a COMPLETE new state
public void Delete() //delete entire group of contacts
//api/GroupContact/354/
public ContactModel Get(int id) //get contact id #354
public void PostContact(ContactModel model) //add contact (overwrite if exits)
public void Delete(int id) //delete contact if exists
If you want your URL's to appear nested (eg: /api/Group/Contacts
, /api/Group/Contact
), you can look at this other post I wrote. IMHO, asp.net's routing needs a tune up to support nesting a bit easier ...but that's a different issue;-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With