13

我正在开发一个 JAX-RS API,其中包括一个简单的“Person”表,其中包含“id”和“name”字段,其中“id”与 mysql 数据库中的自动编号相关联。一个典型的用例是发布一个新人。

{"name":"Bob"}例如,JSON 消息的 POST可能会返回{"id":101,"name":"Bob"}.

如果调用者请求包含标识符的对象的 POST 怎么办?看来我的选择是:

  • 拒绝请求无效
  • 删除请求中的id,继续处理
  • 将 POST 视为 UPSERT(更新失败时,删除 ID 并插入)
  • 尝试使用提供的 id 创建新记录

从安全角度来看,最后一个选项似乎很狡猾。如果我使用 mysql,恶意用户可能会在一个请求中将我的自动编号提升到最大值。

应该如何在 REST API 中处理 POST 请求中包含的 id?

4

3 回答 3

6

您绝对应该拒绝所有到达/users/端点的请求。首先出于安全原因(在数据库级别),其次这不是客户生成/建议 ID 的工作。

因此,答案是拒绝请求无效以及适当的状态代码 ( 400) 和解释拒绝原因的消息。

第二个选项是不直观的,即发送和 ID(正如我所写的那样,这是一个坏主意) - 不会期望收到它发布的不同 ID。在正文中发送 ID 对请求有意义,PUT并且它假定对象已经创建/存在 - 这是一个更新。

第三个选项将不是 RESTful - REST 中没有 upsert -POST创建新资源。第四个选项根本没有意义——这不是客户提供 ID 的工作。

于 2015-11-02T08:13:02.033 回答
0

真正归结为你希望你的 API 支持什么。

允许创建但也允许替换

发送请求PUT /users/101意味着您允许客户端使用提供的有效负载替换或创建 ID 为 101 的资源。对此的正常响应是,根据来自数据库的响应,如果导致替换,则返回 200,如果导致插入,则返回 201。请注意,这是替换类型更新,而不是部分更新。如果您只需要更新资源的一部分,那么您正在寻找在 RDBMS 中难以实现的 PATCH。

如果你不想要这种行为,那么就不要使用动词

如果您不想支持这种行为,那么最好的办法就是根本不允许该路由使用 PUT 动词。这是 RESTful 要做的事情。如果您已经实现了 HATEOS(实际上是 RESTful 的东西),那么 PUT 动词将不会出现在响应负载的超媒体链接中,并且如果您尝试使用它,将返回 HTTP 405 Method Not Allowed。

你真的是 RESTful 的吗?

如前所述,您可以不允许 ID 并要求它在正文中,但这不是必需的,实际上也不是非常 RESTful。我的意思是——通过HATEOAS的镜头来看待它。很容易争辩说,如果客户端需要知道 ID 应该在 PUT 的正文中(甚至可能是 ID 的名称,例如,如果您将其称为“UserID”),那么这基本上会忽略超媒体的整个目标,客户端不需要知道任何关于 API 的信息来导航它。也就是说,基本上没有人实现 HATEOAS/超媒体,但是当试图就什么是 RESTful 和什么不是 RESTful 进行争论时,这就是 IMO 的指路明灯。在真正的 RESTful API 中,客户端只需要知道资源路径和被操作实体的形状。

客户端提供的资源 ID 的问题在于,人们试图提出他们不可能知道整数标识中的下一个数字的论点。对此我要说- 嗯.. 那是你的问题。看在上帝的份上,请停止使用 PK 作为您的公共 ID。实体 ID 应该是全局唯一的,因此让客户端生成一个 GUID 以使用,例如,或者在您的服务器端为 POST 执行此操作。将其用作您的“应用程序实体 ID”。那应该是您在应用程序之间传递的 ID。你可以整天争论性能,但是 a) 对 MongoDB 这样的数据库几乎没有明显的影响,在 RDBMS 中几乎没有任何明显的影响 b) 你没有将表与应用程序 ID 链接在一起——这是一个 where 子句。您仍然在数据库端使用数据库 ID/PK,只是不要 不要把它泄露到野外。但不管怎么说...

当您要创建新资源且不允许客户端设置 ID 时

因此,如果您的意图是允许客户端创建资源,请使用 POST。为此,不应在路由中提供 ID(例如POST /users/101),因为这没有多大意义。如果您需要允许他们设置 ID,则按上述方式实现 PUT。POST 的正确响应是201 Created是否成功,并包含一个Location标头,供客户端执行 GET 以检索其新资源。实际上,有一种称为“PRG”(Post-Redirect-Get)的模式,您可以在其中返回 a303 See Other来为您解决问题。这在 Web/前端上下文中效果最好,但如果客户端将自动跟随重定向,它也可以与 Client-API 一起使用。或者,您可以简单地返回 201,并在正文中包含新资源并在其中填充新 ID。

于 2021-09-08T22:33:13.187 回答
0

另一种选择是使用 @JSonIgnore 注释忽略它。在 GET、PUT 或 DELETE 上,您将拥有 /user/{id},因此客户端应该已经知道 ID。在 POST 上,客户端不应发送 ID,您应该返回 ID,或者更确切地说,您可以获取包含 ID 的已创建对象的 URL。

于 2015-11-04T04:44:29.087 回答