我正在开发一个 Web 分布式应用程序,让任何用户都可以玩 Klondike(纸牌)游戏。我想开发一个 API REST,但我不确定资源 URL 应该是什么样子。
Oracle:您将如何将其实现为网站?
在服务器上,我有“游戏”、“库存”、“废物”、“画面”、“基础”等课程。游戏类有方法moveFromStockToWaste、moveFromTableauToTableu...实现了游戏的动作。
要意识到的重要一点是,您的实现中的类无关紧要; REST 是关于操作文档的,游戏中发生的变化是操作“文档”的副作用。
换句话说,REST API 是您的游戏所戴的面具,因此它看起来像一个网站。
请参阅 Jim Webber 的演讲 DDD In the Large。
Klondike 实际上是一个状态机;任何给定的画面都有一些有限的合法动作要做出,每一个动作都会把你带到一个新的位置。
因此,您可以为 API 建模的一种方式是表示画面以及每个动作的可供性(链接),当您跟随描述可能的合法动作的链接时,游戏会从一个状态进展到下一个状态。
“只有”8*10^67 左右的交易需要担心,对于每个交易,您实际上都有一个所有可到达位置的图表,并按遍历顺序对它们进行排序,然后将它们全部链接起来在一起。
/76543210987654321098765432109876543210987654321098765432109876543210/0
/76543210987654321098765432109876543210987654321098765432109876543210/1
/76543210987654321098765432109876543210987654321098765432109876543210/2
/76543210987654321098765432109876543210987654321098765432109876543210/3
等等。
这不是impossible 的安排,虽然它可能不切实际,而且由于 URL 描述了游戏的整个状态,因此玩家可以访问隐藏状态。
我建议先在不太复杂的地方尝试这种方法,例如 tic-tac-toe。
隐藏状态相对简单,因为当前游戏到特定种子的映射可以在服务器上完成。也就是说,你向开始游戏的终点发送一个 POST,然后生成一些随机标识符,并将随机标识符映射到一个种子位置,然后就可以开始了。
但这种设计中的一个潜在问题是 HTTP 是一种无状态协议;当玩家请求GET /games/000/152 时,服务器无法“知道”客户端先前处于可以合法移动到位置 152 的位置。您可以设置 URI hard to guess,仅此而已。
你可能想要的是能够确保玩家做出的动作是合法的,这意味着服务器需要跟踪游戏的当前状态,并且玩家得到一个仅查看公开信息。
最简单的 HTML 模型将让游戏的表示显示玩家被允许的信息,以及一个包含合法移动列表的表单。玩家选择一个动作并提交表单,这是一个返回游戏资源的 POST(直接返回相同的资源,因为我们想要缓存失效属性)。然后,您的实现可以检查接收到的移动是否合法,刷新自己的本地状态,并发送适当的响应。
这是我们应该考虑的基本模式;获取游戏,然后发送unsafe request 以修改服务器的游戏副本。
如果您想使用远程创作方法,基本计划并没有什么不同。 GET 获取所揭示信息的表示,客户端对该表示进行合法编辑,并将新表示放入相同的 URL。服务器验证是否可以从旧位置到达新位置,并接受移动,使用它自己的隐藏信息副本来更新玩家视图的表示。
(注意PUT 的响应中使用的元数据;无论是采用新的表示,还是服务器已经转换了提议的表示以使其一致,服务器都应该仔细沟通与服务器的约束)。
当然,您也可以使用PATCH 来传达客户端对表示所做的更改。
如果消息丢失或重复,客户端的视图和服务器的视图可能不一致。因此,您可能希望您的游戏表示包含时钟/计时器/回合编号,以便服务器可以确定玩家的移动是针对当前游戏状态的。
EDIT 正如 Roman 所说,HTTP 已经在其中内置了 validators 的概念,它允许您将数据从您的域特定时钟提升到标头中,以便泛型组件能够理解conditional requests并采取适当行动
另一种思考游戏的方式是考虑event sourcing;客户端和服务器轮流将条目附加到log,并通过应用日志中的事件来计算游戏本身的视图。客户端的移动将被限制在操纵公开信息的集合中,服务器的移动将揭示之前隐藏的信息。
所以您可以使用Atom Pub 或类似的东西将新条目写入日志。这实际上为您提供了游戏的两种不同表示形式 - 视图(向您显示您在查看画面时所看到的内容)和提要(向您显示达到该点所采取的动作)。
(如果你眯着眼睛,你会发现这实际上只是“让客户选择合法的举动”的变体。)
您可以,我想,将域模型中的每个元素都视为资源,并尝试设计一个 API 以允许客户端直接操作这些元素,但它不是我很清楚你能从中得到什么好处。