如何为状态机或有限自动机实现 RESTful 资源
我是 Rails 和 REST 新手,我正在尝试找出如何最好地公开由具有状态机(换句话说是有限自动机)的域对象支持的资源。
我见过许多使模型类成为状态机的 gem,例如 aasm、转换、工作流,但它们都没有记录如何在面向资源的控制器中实际使用它们的示例。它们似乎都暗示状态转换是由“事件”触发的,这实际上是一个方法调用。我对这意味着什么有一些疑问:
- 更新操作(PUT 方法)不合适,因为 PUT 被认为是幂等的。唯一可能的情况是状态作为表示的一部分发送。这与“事件”不一致。这是正确的吗?
- 由于事件不是幂等的,因此必须使用 POST。但是,到哪个资源呢?每个可能的事件都有一个子资源吗?或者,是否有一个(/updatestate)将要触发的事件以及该事件的任何参数作为其表示?
- 由于资源的状态是由另一个资源可能触发的事件修改的,因此创建操作是否应该接受对状态属性(或依赖于状态机的任何其他属性)的更改?
- [更新的问题] 在 UI 中公开过渡的好方法是什么?由于事件不是状态,因此允许更新状态属性(以及依赖于状态转换的任何其他属性)似乎没有意义。这是否意味着更新操作中应忽略这些属性?
I'm a Rails and REST newbie and I'm trying to figure how best to expose a resource that is backed by a domain object that has a state machine (in other words is a finite automata).
I've seen a number of gems for making a model class a state machine, such as aasm, transitions, workflow, but none of them document examples of how they are actually used in a resource oriented controller. They all seem to imply that state transitions are triggered by an "event" , which is really a method call. Some questions I have with what this implies are:
- The update action (PUT method) is not appropriate because PUT is suppose to be idempotent. The only this would be possible is if the state was sent as part of the representation. This is inconsistet with an "event". Is this correct?
- Since, events aren't idempotent, then the a POST must be used. But, to which resource? Is there a subresource for each possible event? Or, is there one (/updatestate) that takes as its representation the event to trigger and any parameters to the event?
- Since the state of the resource is modified by an event triggered potentially by another resource, should the create action accept changes to the state attribute (or any other attributes that are dependent on the state machine)?
- [Updated question] What is a good way to expose the transitions in the UI? Since events aren't states, it would seem that it doesn't make sense to allow the state attribute (and any other attribute that is dependent on state transitions) to be updated. Does that mean that these attributes should be ignored in the update action?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
正确的。
你可以用两种方法来做。您可以在同一个应用程序中支持这两者,事件类型的变化由传入文档或接收资源决定。就我个人而言,我更喜欢通过不同的文档类型来完成此操作,但这只是我的意见。如果您选择多个资源路线,请确保它们是可发现的(即,通过获取其父资源时返回的文档中描述的每个资源的链接)。
由你决定;没有真正的理由让你必须密切关注创作的任何特定属性。 (您可以通过说状态在创建后立即更改为状态机的正确初始状态来合理化这一点。)在我完成的状态机中,无论如何创建都是通过 POST 进行的(并且是一个不同的 - 相当复杂的 -文档),所以整个事情没有实际意义,但如果您允许多个初始状态,那么在创建文档中采用“这是我首选的起始状态”提示是有意义的。需要明确的是,仅仅因为用户想要它并不意味着你必须这样做;而是因为用户想要它。当你拒绝用户的建议时,是否要向用户投诉是你的决定。
[库存答案。]
Correct.
You can do it both ways. You can support both in the same application, with variation in event types being determined by either the incoming document or the receiving resource. Personally, I would prefer to do it by differing document types, but that's just my opinion. If you do go the multiple resources route, make sure they're discoverable (i.e., by having links to each of them described in the document returned when you GET their parent resource).
Up to you; there's no real reason why you have to pay close attention to any particular attribute on creation. (You could rationalize this by saying that the state changes to a proper initial state for the state machine immediately after creation.) In the state machines I've done, the creation was by a POST anyway (and of a different – rather complex – document) so the whole thing was moot, but if you allow multiple initial states then it makes sense to take a “this is my preferred starting state” hint in the creation document. To be clear, just because the user wants it doesn't mean you have to do it; whether you want to complain to the user when you reject a suggestion of theirs is your call.
[Stock answer.]
参加聚会有点晚了,但我正在为自己研究这个确切的问题,发现我目前用来管理状态机的 gem (pluginaweek 的 state_machine)有一些方法可以很好地处理这个问题。
当与 ActiveRecord 一起使用时(我假设还有其他持久层),它提供了一个
#state_event=
方法,该方法接受您想要触发的事件的字符串表示形式。请参阅此处的文档。这使您可以简单地在资源的编辑表单中添加一个
state_event
字段,并像更新任何其他属性一样轻松地获得状态转换。现在我们显然仍然使用 PUT 来使用这种方法来触发事件,这不是 RESTful。然而,gem 确实提供了一个有趣的示例,至少“感觉”相当 RESTful ,尽管它在幕后使用相同的非 RESTful 方法。
正如您可以在此处和此处,gem 的内省功能允许您在表单中呈现您想要触发的事件或该事件的结果状态的名称。
使用后一种技术,您可以将模型状态简单地基于表单更新为任何有效的下一个状态,而无需编写任何额外的代码。从技术上讲,它不是 RESTful,但它允许您在 UI 中轻松地以这种方式呈现它。
这种技术的简洁性加上尝试将基于事件的状态机转换为简单的 RESTful 资源时固有的冲突足以让我满意,所以希望它也能为您提供一些见解。
A little late to the party here, but I was researching this exact issue for myself and found that the gem I'm currently using to manage my state machines (state_machine by pluginaweek) has some methods that deal with this issue quite nicely.
When used with ActiveRecord (and I'm assuming other persistence layers as well), it provides a
#state_event=
method that accepts a string representation of the event you would like to fire. See documentation here.This allows you to simply add a
state_event
field in your resource's edit forms and get state transitions as easily as updating any other attribute.Now we're obviously still using PUT to trigger events using this method, which isn't RESTful. The gem does, however, provide an interesting example that at least "feels" quite RESTful, despite it using the same non-RESTful method under the covers.
As you can see here and here, the gem's introspection capabilities allow you to present in your forms either the event you would like to fire or the name of that event's resulting state.
Using the latter technique, you get simple form-based updating of the model's state to any valid next state without having to write any extra code. It's not technically RESTful, but it allows you to easily present it that way in the UI.
The cleanliness of this technique combined with the inherent conflicts in trying to cast an event-based state machine into a simple RESTful resource was enough to satisfy me, so hopefully it provides some insight to you as well.
参加这里的聚会有点晚了,而且离专家还很远,因为我有类似的疑问,但是......
将活动作为资源怎么样?
因此,
您可以...
在 Order 和 OrderEvent 之间使用 pub/sub 侦听器,或者尝试在 Order 上触发该事件并记录转换消息的回调。它还可以让您方便地审核所有状态更改事件。
创意来自 Shopify 的 Willem Bergen
我错过了什么吗?抱歉,我自己很难理解这一点。
Bit late to the party here and far from an expert as I have a similar query but...
How about making the event a resource?
So instead of...
You would...
With a pub/sub listener between Order and OrderEvent or callback that attempts to fire that event on Order and records the transition messages. It also gives you a handy audit of all state change events.
Idea stolen from Willem Bergen at Shopify
Am I missing something? Sorry, struggling to understand this myself.
如果您的资源具有某种状态属性,您可以使用一种称为 micro-PUT 的技术来更新其状态。
您可以将资源状态建模为集合并在这些集合之间移动资源。
您始终可以使用新的 PATCH 方法
If your resource has some kind of status attribute, you can use a technique called micro-PUT to update it's status.
You can model resource states as collections and move resources between those collections.
You could always use the new PATCH method