2013-04-18 50 views
11

我想为使用ASP.NET Web API的服务设计一个REST风格的Web API。我在计算如何将非CRUD操作路由到适当的控制器操作时遇到了麻烦。假设我的资源是一扇门。我可以用我的门做所有熟悉的CRUD。假设模型我的门是:如何在RESTful ASP.NET Web API中路由非CRUD操作?

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

我可以做所有我的标准CRUD操作的通过我的网络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 

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

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

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

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

在这种情况下,我仍然可以使用建议{控制器}/{ID}/{行动},但它好像我还是创建一个混合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

谷歌使用REST API与Blogger,蚂蚁在它使用REST行动! https://developers.google.com/blogger/docs/3.0/reference/posts/publish – padibro

回答

6

要处理lock/unlock情况下,您可以考虑增加一个State属性的Door对象:

public State State { get; set; } 

其中处于可用状态值的枚举,例如

{ 
LockedFromOutsideRoom, 
LockedFromInsideRoom, 
Open 
} 

澄清:作为状态在API每次拨打电话做一些同门时间的推移,您要添加的状态的对象不是针对宁静的原则。

然后通过api你会发送一个PUT/POST请求来改变每个锁定/解锁的门状态。帖子可能会更好,因为它是唯一一个被更新的属性:

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

我需要在WebApiConfig.cs中为此注册一个自定义路由吗?这似乎仍然遵循{controller}/{id}/{action}的模式,其中我的控制器现在具有DoorsController :: State(long id,State state);方法。 – JadeMason

+1

@JadeMason - 这条路线应该正常工作 –

+0

@JoannaTurban很好的答案!我甚至可以用PUT做到这一点,就像这样:'PUT:http://api.contoso.com/v1/doors/1234 体:{ “状态”: “LockedFromInsideRoom”}'因为你实际上* *更新了州。此外,感觉与PUT的[idempotence](http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods_and_web_applications)更加一致:每次请求该URL都会产生完全相同的结果。 –

1

从你可能要被处理锁在本身资源的REST的观点。通过这种方式,您可以独立于门创建和删除锁(尽管大概从门表示中找到锁端点)。资源的URL可能与门的URL有关,但从RESTful的角度来看,这无关紧要。 REST是关于资源之间的关系,所以重要的部分是锁的URL可以从门的表示中发现。

2

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

每回答这样的问题,看起来你必须使用一个变通执行你的API设计符合REST风格。但我担心的是,这是否真的为用户和开发人员带来便利?我们来看看Google Bloger的API3.0设计:https://developers.google.com/blogger/docs/3.0/reference,它使用非URL CURD操作的地址URL。

这是有趣的,

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

和描述是

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

你可以看到,评论有一个状态来表明它是否是垃圾邮件,但它的设计不像JoannaTurban上面提到的答案。

我觉得从用户的观点来看,它更方便。不需要关心“状态”的结构和枚举值。实际上,你可以在“状态”的定义中加入很多属性,如“isItSpam”,“isItReplied”,“isItPublic”等。如果状态有很多东西,设计就会变得不友好。

在一些业务逻辑的要求,用一种易于理解动词,而不是试图使之完全一个“真正的” REST风格,它更高效,既为用户和开发人员。这是我的意见。