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

Jad*_*son 13 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; }
}
Run Code Online (Sandbox Code Playgroud)

我可以通过我的web 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
Run Code Online (Sandbox Code Playgroud)

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

PUT: http://api.contoso.com/v1/doors/1234/lock
PUT: http://api.contoso.com/v1/doors/1234/unlock
Run Code Online (Sandbox Code Playgroud)

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

POST: http://api.contoso.com/v1/doors/1234/lockrequests
POST: http://api.contoso.com/v1/doors/1234/unlockrequests
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我仍然可以使用推荐{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
Run Code Online (Sandbox Code Playgroud)

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

PUT: http://api.contoso.com/v1/doors?lock&InsideRoomName=Cafeteria
Run Code Online (Sandbox Code Playgroud)

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

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

Fan*_*Fan 8

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

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

让我们看一下Google bloger的API3.0设计:https://developers.google.com/blogger/docs/3.0/reference,它使用lot URL进行非CURD操作。

这很有趣,

POST  /blogs/blogId/posts/postId/comments/commentId/spam
Run Code Online (Sandbox Code Playgroud)

描述是

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

您可以看到,评论有一个状态来指示它是否是垃圾邮件,但它的设计与 JoannaTurban 上面提到的答案不同。

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

在某些业务逻辑需求上,使用易于理解的动词,而不是试图使其完全成为“真正的”RESTful,对于用户和开发人员来说,它的生产力更高。这是我的意见。


Joa*_*rks 6

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

   public State State { get; set; }
Run Code Online (Sandbox Code Playgroud)

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

{
LockedFromOutsideRoom,
LockedFromInsideRoom,
Open
}
Run Code Online (Sandbox Code Playgroud)

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

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

POST: http://api.contoso.com/v1/doors/1234/state
body: {"State":"LockedFromInsideRoom"}
Run Code Online (Sandbox Code Playgroud)