【问题标题】:REST API for a game游戏的 REST API
【发布时间】:2018-09-23 14:35:39
【问题描述】:

我正在开发一个 Web 分布式应用程序,让任何用户都可以玩 Klondike(纸牌)游戏。我想开发一个 API REST,但我不确定资源 URL 应该是什么样子。

在服务器上,我有“游戏”、“库存”、“废物”、“画面”、“基础”等课程。游戏类有方法moveFromStockToWaste、moveFromTableauToTableu...实现了游戏的动作。

我读到的关于 REST API 的内容是,资源 URL 应该看起来像一个名词层次结构,而对这个名词的操作(动词)是 HTTP 方法(GET、PUT、POST、PATCH)。 尽管 moveFromTableauToTableau 资源是动词而不是名词,但我不确定通过 API REST 将卡片从库存转移到废品的方式是否应该如下所示:

 UPDATE /player/{playerId}/game/{gameId}/moveFromTableauToTableau

我的另一种方法是让这个画面堆成卡片资源:

 URL: /player/{playerId}/game/{gameId}/TableauPile/1/ 

依次拥有诸如未上翻卡片和上翻卡片数量之类的资源(有关画面所需的所有信息)。 然后通过删除最后一张卡片来更新这个画面堆资源:

DELETE /player/{playerId}/game/{gameId}/TableauPile/1/upTurnedCard/3

然后将删除的卡牌放入传递新卡套和价值的目标中:

POST /player/{playerId}/game/{gameId}/TableauPile/3/upTurnedCard

但这种方式 REST API 会允许将卡​​片从画面移动到垃圾箱,这不是有效的移动。

【问题讨论】:

    标签: angularjs spring rest web-services web


    【解决方案1】:

    我一直认为设计一个 REST API 是一项非常不容易的任务。

    第二种方法在命名约定方面看起来更简洁,但我认为您永远不应该为了符合这些要求而牺牲目标系统的完整性。由于您允许在两个 http 调用中进行一个原子操作,它实际上不再是原子的,并且在网络故障或任何调用由于某种原因失败的情况下,您将系统暴露于不可预测的状态。避免此类问题必须是重中之重。

    一个想法可能是在移动集合方面考虑移动。所以对于一个游戏,你有一个动作资源。然后您可以使用一些额外的请求参数来优化移动性质,例如

    POST/players/{playerId}/games/{gameId}/moves?type=TABLEAU_TO_TABLEAU
    Body: 
    {
      "src": "stock",
      "dest": "waste"
    }
    

    这样你应该有足够的灵活性来处理不同类型的移动。

    另外,我建议你使用复数形式来命名资源:

    玩家 -> 玩家

    游戏 -> 游戏

    所以

    GET /players 自然意味着给我玩家资源的所有值

    GET /players/1 表示给我限制 playerId=1 的玩家资源的玩家

    【讨论】:

    • 所以在您的提案中,移动将是一个资源,它的表示是一个 JSON,其中包含移动的原点和目标的数据,然后客户端通过更新这个移动的表示和一些来更改资源禁止表示(某些来源和目标的组合)。我理解对了吗?那么客户选择后如何执行移动呢?只需更新此资源就应该执行移动?
    • 是的,就是这样。在底层服务层中,您可以排除禁止移动。您也可以发布 GET /players/{playerId}/games/{gameId}/moves 以根据游戏状态返回所有合法移动。是的,只需更新移动资源。我想您可以将 PUT 切换为 POST。当您在游戏中创建新动作时,它可能具有更好的含义。所以这是一个POST。我编辑了我的答案。
    【解决方案2】:

    我正在开发一个 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 以允许客户端直接操作这些元素,但它不是我很清楚你能从中得到什么好处。

    【讨论】:

    • 只是附加在这个详细的答案上,“时钟/时间/转弯”机制可以通过 ETag 或 If-Modified-Since 标头(或类似)在条件请求之上实现,以重用已经建立的并发机制形式已经在 HTTP 中可用。这个答案还表明,REST 不仅仅是通过 HTTP 公开数据库模型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-24
    • 2018-03-02
    • 2022-01-12
    • 2011-02-18
    相关资源
    最近更新 更多