Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Route non-CRUD actions in a RESTful ASP.NET Web API?

I am trying to design a RESTful web API for our service using ASP.NET Web API. I'm running into trouble with figuring out how to route non-CRUD actions to the proper controller action. Let's assume my resource is a door. I can do all of the familiar CRUD things with my door. Let's say that model for my door is:

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

I can do all of my standard CRUD operations via my 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

and so on. Where I run into trouble is when I need to model the non-CRUD actions against my door. I want to model a Lock and Unlock verb against my resource. Reading through the ASP.NET articles the guidance seems to be to switch to an RPC style call when using custom actions. This gives me a path:

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

This seems to conflict with the spirit of REST which aims for the path to indicate a resource. I suppose I could model the verb as a resource:

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

In this case I could still use the recommend {controller}/{id}/{action} but it seems like I'm still creating a mixed RPC / REST API. Is it possible, or even recommended as far as REST interfaces go, to put the custom action in the list of parameters?

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

I could foresee a need to have this call supported with query parameters as well, such as:

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

How would I create the route to map this request to my 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 may be making some false assumptions here regarding what is and is not best practices with respect to REST API design, so any guidance there is appreciated as well.

like image 710
JadeMason Avatar asked Apr 18 '13 19:04

JadeMason


People also ask

Is REST API only for CRUD?

CRUD functions can exist in a REST API, but REST APIs are not limited to CRUD functions. CRUD can operate within a REST architecture, but REST APIs can exist independent of CRUD. For example, a REST API can allow clients to reboot a server even if it doesn't correspond to any CRUD functions.

What is routing in REST API?

Routing is a functionally based tag or Uri template used by APIs to match the desired action or methods expected to be executed. There are two types or rather two different types of Routing being used during development.


1 Answers

From RESTful principle, maybe it's best to introduce a 'status' property to manage those non-CURD actions. But I don't think it meets the real production development.

Every answer to this kind of question, looks like you must have to use a work-around to enforce your API design meets RESTful. But my concern is, is that really making convenience to both user and developer?

let's take a look on the API3.0 design of Google bloger: https://developers.google.com/blogger/docs/3.0/reference, it's using lot URL for non-CURD actions.

And this is interesting,

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

and the description is

Marks a comment as spam. This will set the status of the comment to spam, and hide it in the default comment rendering.

You can see, a comment has a status to indicate whether it's a spam or not, but it was not designed like the answer mentioned above by JoannaTurban.

I think from user point of view, it's more convenient. Don't need to care the structure and the enum value of the "status". And actually you can put lot of attributes into the definition of "status", like "isItSpam", "isItReplied", "isItPublic" etc. The design will becomes unfriendly if the status has many things.

On some business logic requirement, to use an easy to understand verb, instead of trying to make it completely a "real" RESTful, it's more productive, for both user and developer. This is my opinion.

like image 170
Fan Avatar answered Sep 18 '22 16:09

Fan