假设您有一个Person资源,其部分表示包含一个Location值,其值可以是"at home","at school"和"at work".你会如何重新开展"回家","上班","上学"等活动?为了便于讨论,让我们规定这些活动需要时间,因此它们是异步执行的,并且有多种方式可以使它们失败(没有交通方式,旅行期间的交通故障,上帝的其他行为等) .此外,Person资源还具有影响这些属性的其他属性和相关操作(例如,attribute = energy-level,operations = eat/sleep/excercise).
选项1:在Person资源上重载POST,提供一个输入参数,指示您希望该人做什么(例如,action = go-to-school).从POST返回202并在Person的表示中公开活动进行中状态属性,客户端可以获取该属性以观察进度和成功/失败.
好处:保持简单.
缺点:相当于隧道效应.发生的操作隐藏在有效负载中,而不是在URI,动词,标题等中可见.此资源上的POST动词没有单一的语义含义.
选项2:使用PUT将人员的位置设置为您希望他们拥有的状态.从PUT返回202并通过GET公开状态轮询的活动进行中属性.
好处:不确定我看到了什么.
缺点:实际上,这只是与另一个动词的隧道效应.此外,它在某些情况下不起作用(睡眠和进食都会增加能量水平,因此将能量水平设置为更高的值在您希望资源执行的操作方面是不明确的).
选项3:公开对Person对象进行操作的通用控制器资源.例如,创建一个PersonActivityManager资源,该资源接受带有标识目标Person和请求操作的参数的POST请求.POST可以返回PersonActivity资源来表示正在进行的活动,客户端可以通过GET来监视进度和成功/失败.
好处:通过将活动及其状态与Person资源分开,看起来更清晰.
缺点:现在我们已经将隧道移动到PersonActivityManager资源.
选项4:为每个支持的操作建立单独的控制器资源,例如,接受带有标识Person的参数(或URI元素)的POST请求的ToWorkTransporter资源,以及ToHomeTransporter,ToSchoolTransporter,MealServer,Sleeper和Exerciser.其中每个都从POST方法返回一个适当的任务监视资源(Commute,Meal,Slumber,Workout),客户端可以通过GET监视它.
好处:好的,我们终于消除了隧道效应.每个POST只意味着一件事.
缺点:现在谈论了很多资源(也许我们可以将传输器合并到一个接受目标参数的传输器中).其中一些是非常语义上的设计(一个沉睡者?).它可能更RESTful,但它是否实用?
And*_*nie 18
好的,我一直在研究和思考这个问题大约一个星期了.由于没有其他人回答,我会发布我所学到的结果.
RESTful Casuistry的 Tim Bray 讨论了将状态字段与POST进行控制,该控制器将执行影响该状态的操作.他使用VM的示例以及如何RESTly公开"重启按钮"的功能.他说
"如果我想更新现有资源中的某些字段,我倾向于考虑PUT.但这不起作用,因为它应该是幂等的,并且重新启动服务器肯定不是.好吧,好吧,做吧用POST我猜;没什么大不了的.
但是你并没有真正改变一个国家,你要求发生一系列特定的行动,因此国家可能会或可能不会达到预期的价值.实际上,当您点击部署交换机时,状态会更改为部署,然后在部署之后会有一些不可预测的时间.重启操作是一个盒子的经典案例,侧面有一个大红色开关; 问题是如何按下开关.
所以,我越想到它,我就越认为这些资源就像按钮一样,只有一个定义的操作:push.人们一直抱怨"只写资源",但我没有问题,因为它看似准确.重启和暂停按钮实际上没有任何状态,所以你不应该期待GET中有用的东西."
Tim似乎在我的#3和#4选项之间解决了问题,暴露了多个控制器资源,但是从"过度"退出并为所有内容分别拥有控制器资源.
蒂姆的帖子由Roy Fielding(可以使用POST)导致另一个帖子,他说,对于有可监控实体状态的情况,以及可能改变该状态的行为,他倾向于使用POST而不是PUT.他说,针对一位评论者建议将受监控状态作为单独的PUT资源公开
"当更新操作是幂等的并且表示完成时,我们只使用PUT.我认为每当我们认为资源可能对其他人孤立有用时,我们应该定义一个额外的资源,并为该资源使用GET/PUT方法,但我认为我们不应仅仅为了避免POST来定义新资源."
最后,Bill de hOra,在Just use POST中讨论了使用PUT与POST来更新集合资源状态的具体情况,以及其中的权衡.