我正在尝试使用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设计的最佳实践的错误假设,因此任何指导都会受到赞赏。
答案 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可以从门的表示中被发现。