我正在为客户管理系统编写一个RESTful服务,并试图找到部分更新记录的最佳实践。例如,我希望调用者能够使用GET请求读取完整的记录。但是为了更新它,只允许对记录进行某些操作,比如将状态从ENABLED更改为DISABLED。(我有比这更复杂的场景)
出于安全原因,我不希望调用者只提交更新字段的整个记录(这也感觉有点过分)。
是否有构造uri的推荐方法?在阅读REST书籍时,RPC样式的调用似乎是不受欢迎的。
如果下面的调用返回id为123的客户的完整客户记录
GET /customer/123
<customer>
{lots of attributes}
<status>ENABLED</status>
{even more attributes}
</customer>
我该如何更新状态?
POST /customer/123/status
<status>DISABLED</status>
POST /customer/123/changeStatus
DISABLED
...
更新:扩充问题。如何将“业务逻辑调用”合并到REST api中?有没有大家都同意的做法?并非所有的方法本质上都是CRUD。有些更复杂,如“sendEmailToCustomer(123)”,“mergeCustomers(123, 456)”,“countCustomers()”
POST /customer/123?cmd=sendEmail
POST /cmd/sendEmail?customerId=123
GET /customer/count
没关系。就REST而言,你不能做GET,因为它不是可缓存的,但不管你使用POST, PATCH, PUT或其他什么都没关系,URL看起来是什么也没关系。如果您正在使用REST,那么重要的是当您从服务器获得资源的表示时,该表示能够提供客户端状态转换选项。
如果您的GET响应有状态转换,客户机只需要知道如何读取它们,服务器可以根据需要更改它们。这里的更新是使用POST完成的,但是如果它被更改为PATCH,或者如果URL更改了,客户端仍然知道如何进行更新:
{
"customer" :
{
},
"operations":
[
"update" :
{
"method": "POST",
"href": "https://server/customer/123/"
}]
}
你可以列出必要的/可选的参数,让客户端反馈给你。这取决于应用程序。
就业务操作而言,这可能是与客户资源链接的不同资源。如果你想发送一封电子邮件给客户,也许该服务是它自己的资源,你可以POST,所以你可以在客户资源中包括以下操作:
"email":
{
"method": "POST",
"href": "http://server/emailservice/send?customer=1234"
}
以下是演示者REST架构的一些优秀视频和示例。Stormpath只使用GET/POST/DELETE,这很好,因为REST与你使用的操作或url的外观没有任何关系(除了GET应该是可缓存的):
https://www.youtube.com/watch?v=pspy1H6A3FM,
https://www.youtube.com/watch?v=5WXYw4J4QOU,
http://docs.stormpath.com/rest/quickstart/
使用PUT更新不完整/部分的资源。
您可以接受jObject作为参数,并解析其值以更新资源。
下面是你可以参考的Java函数:
public IHttpActionResult Put(int id, JObject partialObject) {
Dictionary < string, string > dictionaryObject = new Dictionary < string, string > ();
foreach(JProperty property in json.Properties()) {
dictionaryObject.Add(property.Name.ToString(), property.Value.ToString());
}
int id = Convert.ToInt32(dictionaryObject["id"]);
DateTime startTime = Convert.ToDateTime(orderInsert["AppointmentDateTime"]);
Boolean isGroup = Convert.ToBoolean(dictionaryObject["IsGroup"]);
//Call function to update resource
update(id, startTime, isGroup);
return Ok(appointmentModelList);
}
关于你的更新。
我认为CRUD的概念在API设计方面造成了一些混乱。CRUD是对数据执行基本操作的一般低级概念,HTTP谓词只是请求方法(创建于21年前),可能映射到CRUD操作,也可能不映射到CRUD操作。事实上,请尝试在HTTP 1.0/1.1规范中找到CRUD首字母缩写。
在谷歌云平台API文档中可以找到一个应用实用约定的解释很好的指南。它描述了创建基于资源的API背后的概念,一个强调大量资源而不是操作的API,并包括您正在描述的用例。虽然这只是他们产品的一种惯例设计,但我认为这很有意义。
这里的基本概念(也是产生很多混淆的概念)是“方法”和HTTP动词之间的映射。一件事是定义API将对哪种类型的资源执行什么“操作”(方法)(例如,获取客户列表或发送电子邮件),另一件事是HTTP动词。您计划使用的方法和动词必须有一个定义,以及它们之间的映射。
它还说,当一个操作与标准方法(在本例中是List、Get、Create、Update、Delete)不完全匹配时,可以使用“自定义方法”,比如BatchGet,它根据多个对象id输入检索多个对象,或者SendEmail。