如何在RESTful ASP.NET Web API中路由非CRUD操作?

时间:2013-04-18 19:56:56

标签: rest asp.net-web-api

我正在尝试使用ASP.NET Web API为我们的服务设计RESTful Web API。我在弄清楚如何将非CRUD操作路由到正确的控制器操作时遇到了麻烦。让我们假设我的资源是一扇门。我可以用我的门完成所有熟悉的CRUD事情。让我们说我门的模型是:

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

我可以通过我的网络API执行所有标准CRUD操作:

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

等等。我遇到麻烦的地方是我需要对我门上的非CRUD动作进行建模。我想针对我的资源建模一个Lock和Unlock动词。阅读ASP.NET articles指南似乎是在使用自定义操作时切换到RPC样式调用。这给了我一条路:

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

这似乎与REST的精神相冲突,REST旨在指明资源的路径。我想我可以将动词建模为资源:

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

在这种情况下,我仍然可以使用推荐的{controller} / {id} / {action},但似乎我仍然在创建一个混合的RPC / REST API。就REST接口而言,是否可以将自定义操作放在参数列表中?

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

我可以预见还需要使用查询参数支持此调用,例如:

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

如何创建将此请求映射到我的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);
}

我可能会在这里做一些关于REST API设计的最佳实践的错误假设,因此任何指导都会受到赞赏。

3 个答案:

答案 0 :(得分:6)

要处理lock/unlock方案,您可以考虑向State对象添加Door属性:

   public State State { get; set; }

其中State是可用值的枚举,例如

{
LockedFromOutsideRoom,
LockedFromInsideRoom,
Open
}

澄清:你正在向对象添加一个状态并不违反宁静的原则,因为每当你打电话给门做某事时,状态就会通过api传递。

然后通过api,您会发送PUT/POST请求,以便在每次锁定/解锁时更改门的状态。 Post可能会更好,因为它只有一个属性可以更新:

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

答案 1 :(得分:4)

从RESTful原则,也许最好引入一个“状态”&#39;用于管理这些非CURD操作的属性。但我认为它不符合真正的生产发展。

对此类问题的每个答案,看起来您必须使用解决方法来强制执行您的API设计符合RESTful。但我担心的是,这真的为用户和开发人员带来了便利吗?

让我们来看看Google bloger的API3.0设计:https://developers.google.com/blogger/docs/3.0/reference,它使用批量网址进行非CURD操作。

这很有趣,

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

,描述是

  

将评论标记为垃圾内容。这会将评论的状态设置为垃圾邮件,并将其隐藏在默认评论呈现中。

你可以看到,评论的状态表明它是否是垃圾邮件,但它的设计并不像JoannaTurban上面提到的那样。

我认为从用户的角度来看,它更方便。不需要关心&#34; status&#34;的结构和枚举值。实际上,您可以将许多属性放入&#34; status&#34;的定义中,例如&#34; isItSpam&#34;,&#34; isItReplied&#34;,&#34; isItPublic&#34;如果状态有很多东西,设计将变得不友好。

在一些业务逻辑要求上,使用一个易于理解的动词,而不是试图使它完全成为一个真正的&#34;对于用户和开发人员来说,RESTful,它的工作效率更高。这是我的意见。

答案 2 :(得分:1)

从REST角度来看,您可能希望将锁视为资源本身。这样您就可以独立于门创建和删除锁(尽管可能是从门表示中找到锁定端点)。资源的URL可能与门的URL相关,但是从RESTful的角度来看,这是无关紧要的。 REST是关于资源之间的关系,所以重要的部分是锁的url可以从门的表示中被发现。