在REST中需要更多动词时该怎么做
还有另外一个类似的问题,但讨论偏离了我所遇到的问题。
假设我有一个处理费用报告(ER)的系统。 您可以创建和编辑它们,添加附件并批准/拒绝它们。
开支报告可能如下所示:
GET /er/1
=>
{"title": "Trip to NY", "totalcost": "400 USD",
"comments": [
"john: Please add the total cost",
"mike: done, can you approve it now?"
],
"approvals": [
{"john": "Pending"}, {"finance-group": "Pending"}]
}
这看起来很好,对吧? 这就是费用报告文档的样子。
如果你想更新它,你可以这样做:
POST /er/1
{"title": "Trip to NY 2010"}
如果你想批准它,你可以这样做:
POST /er/1/approval
{"approved": true}
但是,如果您想要更新报告并同时批准,该怎么办? 我们如何做到这一点? 如果您只想批准,那么对/er/1/approval
进行POST
是有道理的。
我们可以在URL中放置一个标志POST /er/1?approve=1
,并将数据更改作为主体发送,但该标志看起来不是RESTful。
我们也可以提交专业领域,但这似乎也有点不合理。 如果我们这样做了,那么为什么不发送带有set_title
或add_to_cost
属性的数据呢?
我们可以创建一个更新和批准的新资源,但是(1)我想不出如何在没有动词的情况下命名它,以及(2)根据可以执行什么操作来命名资源似乎不太合适它(如果我们添加更多操作会发生什么?)
我们可以有一个X-Approve:True | False标题,但标题看起来像是这个工作的错误工具。 在浏览器中使用javascript也很难获得设置标题。
我们可以使用自定义媒体类型, application/approve+yes
,但似乎没有比创建新资源更好。
我们可以创建一个临时的“批处理操作”url, /er/1/batch/A
客户端然后发送多个请求,可能是POST /er/1/batch/A
进行更新,然后POST /er/1/batch/A/approval
以批准,然后POST /er/1/batch/A/status
结束批量。 在后端,服务器将所有批处理请求排队,然后在收到“结束批处理”请求时将它们处理在相同的后端事务中。 显然,它的缺点是它引入了很多复杂性。
那么,什么是解决在单个请求中执行多个操作的问题的好方法? 因为很容易想象在同一请求中可能会做的其他操作:
它也是一个表现问题。 HTTP调用会触发网络(如果您的网络延迟较高或连接不稳定,这可能会造成问题),因此您可以制作的网页越少越好。
REST体系结构表示资源由服务器管理并由URL标识。
在这种情况下,除非您有在服务器端管理和操作的审批对象或实体,否则light /er/1/approval
不是合理的URL或模型。 在我看来,实体就是费用报告本身,这意味着/er/1
是您的URL路径。
现在,对于动词......您可以发送(POST)您喜欢的任何消息给该资源。
设置数据:
{ action: "modify", data: { purpose : "Club hopping" } }
批准:
{ action: "approve" }
新增项目:
{ action:"additem", data: { amount:72.13, category:113, note:"client dinner" }}
等等
从Fielding的定义REST的Ch5中,
参数(请求的)包含请求控制数据,指示请求目标的资源标识符和可选表示。
...和...
控制数据定义组件之间消息的目的, 例如请求的动作或响应的含义。 它也用于参数化请求并覆盖某些连接元素的默认行为。 例如,缓存行为可以通过包含在请求或响应消息中的控制数据来修改。
因此,如果您想对资源执行多项操作,则应该在“控制数据”中嵌入多条消息或操作请求。 在我的示例中,发布的数据将如下所示:
{ action: "modify", data: { purpose : "Club hopping" } }
{ action: "approve" }
但是你可能想要将它概括为:
{ actions: [ {action:"modify", data: {...} }, { action:"approve"} ] }
您的服务器可以在每种特定类型的实体上处理的消息或操作由您定义。
ps:有时REST实现使用HTTP PUT
来创建资源和POST
来修改或操作现有资源。
和:我喜欢这篇文章,如何获得一杯咖啡。
为了操纵资源的状态,我经常喜欢使用“状态桶”。 这个想法是,当你将一个对象“添加”到该存储桶中时,它就会获得该状态。 这就像在桌子上放入和放出盒子一样。 文档的位置定义了其状态。
所以,你可以做一些简单的事情:
POST /Expenses/Approved
{ .. Expense document ... }
或者您在您的文档中暗示多个人必须批准文档的更复杂情况。
POST /ExpenseApprover/John/ApprovedExpenses
{ .. Expense document ... }
如果您需要提交费用报告以进行审批,则可以执行此操作
POST /ExpenseApprover/John/Pending
{ .. Expense document ... }
不要忘记,超媒体可以使这个流程工作流程启用。 想象一下,有人创建了一份初始费用报告,服务器可以用以下JSON响应。
{ "id" : "234",
"title": "Trip to NY", "totalcost": "400 USD",
"submit_url": "/ExpenseApprover/John/Pending"
}
客户端可以POST到submit_url将费用转移到下一步。 然后,当约翰检索费用时,他得到了
{ "id" : "234",
"title": "Trip to NY", "totalcost": "400 USD",
"approve_url": "/ExpenseApprover/Finance/Pending",
"denied_url": "/ExpenseApprover/John/Denied",
}
当财务部门做一个
GET /ExpenseApprover/Finance/Pending
他们可以得到待定费用清单,
{ PendingExpense: [
{ "id" : "234",
"title": "Trip to NY", "totalcost": "400 USD",
"approve_url": "/Expense/Approved",
"denied_url": "/ExpenseApprover/Finance/Denied",
}
]
}
原谅我可怕的JSON,但我希望你能明白,在响应中包含链接可以指导应用程序的流程。 您也可以不再担心网址的外观,因为客户端并不在乎。 客户端根据属性名称从响应中读取url并将其解除引用。 你可以改变你的想法一百万次,最好的网址结构是什么,你的客户不会受到影响。 只是不要更改属性名称!
这些“状态桶”网址用于保存一组具有类似状态的资源。 这个想法是你将一个文档发布到集合中:
POST /ExpenseApprover/Finance/Denied
{"id" : "234", "title": "Trip to NY", "totalcost": "400 USD"}
没有必要唯一地定义您在URL中添加的特定费用,因为正文文档应包含某种标识键值。
这项技术对标记费用有差异同样有效。 您只需创建一个新的资源,以保存差异性开支并将费用报表发布到该资源中。
POST /Discrepancies
{"id" : "234", "title": "Trip to NY", "totalcost": "400 USD"}
在REST中需要更多动词时该怎么做
你有3个选项:
在你的情况下,你错过了一个或两个PATCH方法的RFC。
链接地址: http://www.djcxy.com/p/45347.html