当我们想要将数据和实现隐藏在接口后面时,封装是有意义的。在这里我们要暴露数据的结构,因为它是用于通信的,而不是用于存储的,服务当然需要这种通信才能运行。数据验证是一个非常基本的概念,因为它使服务可靠并且可以防止黑客攻击。这里的 id 是一个参数,检查它的结构只是参数验证,如果失败应该返回 400。因此,这不仅限于请求的正文,问题可能出现在 HTTP 消息中的任何地方,如下所示。另一个反对 404 的论点是所请求的资源不可能存在,因为我们谈论的是格式错误的 id 和格式错误的 URI。验证每个用户输入非常重要,因为格式错误的参数可用于注入,例如如果未验证,则用于 SQL 注入。
超文本传输协议 (HTTP) 400 错误请求响应状态
code 表示服务器不能或不会处理请求
由于某些被认为是客户端错误的事情(例如,
格式错误的请求语法、无效的请求消息帧,或
欺骗性请求路由)。
对比
HTTP 404 Not Found 响应状态码表示服务器
找不到请求的资源。导致 404 页面的链接是
通常称为断开或死链接,并且可能会受到链接腐烂。
404 状态码仅表示资源丢失:没有
缺席是暂时的还是永久的。如果资源是
永久删除,请改用 410 (Gone) 状态。
在 REST 的情况下,我们使用 HTTP 协议、URI 标准、MIME 类型等来描述接口,而不是实际的编程语言,因为它们是独立于语言的标准。对于您的具体情况,最好检查 uniform interface constraints 包括 HATEOAS 约束,因为如果您的服务按照应有的方式生成 URI,那么很明显,格式错误的 id 是恶意的。对于 Spotify 和其他 API,其中 99% 不是 REST API,可能是 REST-ish。阅读菲尔丁论文和标准,而不是试图根据 SO 答案和示例来弄清楚。所以这是一个典型的 RTFM 情况。
在 REST 的上下文中,一个非常简单的数据隐藏示例是存储一个数字,例如:
PUT /x {"value": "111"} "content-type:application/vnd.example.binary+json"
GET /x "accept:application/vnd.example.decimal+json" -> {"value": 7}
在这里,我们不公开我们如何存储数据。我们只是发送它的二进制和十进制表示。这称为数据隐藏。在 id 的情况下,拥有外部 id 并将其转换为内部 id 是没有意义的,这就是为什么您在数据库中使用相同的,但可以检查其结构是否有效。通常您验证它并将其转换为 DTO。
在这种情况下,实现隐藏更加复杂,它有点避免对服务进行微管理,而是在频繁发生时实现新功能。它可能涉及消费者调查,了解他们需要哪些功能,检查日志并找出某些消费者发送过多消息的原因以及如何将它们合并为一条消息。例如,我们有一个数学服务:
PUT /x 7
PUT /y 8
PUT /z 9
PUT /s 0
PATCH /s {"add": "x"}
PATCH /s {"add": "y"}
PATCH /s {"add": "z"}
GET /s -> 24
vs
POST /expression {"sum": [7,8,9]} -> 24
如果你想在结构化编程、OOP 和 REST 之间进行转换,那么它是这样的:
Number countCartTotal(CartId cartId);
<=>
interface iCart {
Number countTotal();
}
<=>
GET api/cart/{cartid}/total -> {total}
所以一个端点代表一个暴露的操作,比如verbNoun(details),例如countCartTotal(cartId),您可以将其拆分为 verb=countTotal、noun=cart、details=cartId 并从中构建 URI。动词必须转换为 HTTP 方法。在这种情况下,使用 GET 是最有意义的,因为我们需要数据而不是发送数据。动词的其余部分必须转换为名词,所以countTotal -> GET totalCount。然后你可以合并两个名词:totalCount + cart -> cartTotal。然后,您可以根据生成的名词和详细信息构建一个 URI 模板:cartTotal + cartId -> cart/{cartid}/total,您就完成了端点设计GET {root}/cart/{cartid}/total。现在您可以将其绑定到countCartTotal(cartId) 或repo.resource(iCart, cartId).countTotal()。
所以我认为如果 id 的结构没有改变,那么如果你愿意,你甚至可以将它添加到 API 文档中。虽然没有必要这样做。
从安全角度来看,如果发送此类请求的唯一可能原因是黑客攻击,则您可以返回 404,因此黑客无法确定失败的原因,并且您不会暴露保护的详细信息。在这种情况下,它会过度思考问题,但在某些情况下它是有意义的,例如API 可能泄漏数据的地方。例如,当您发送密码重置链接时,Web 应用程序通常会要求您提供电子邮件地址,如果未注册,大多数应用程序都会发送错误消息。这可用于检查是否有人在网站上注册,因此最好隐藏此类错误。我猜在你的情况下,id 不是敏感的东西,如果你有适当的访问控制,那么即使黑客知道 id,他们也无法对这些信息做太多事情。
另一个可能的方面是,如果 id 的结构发生变化会怎样。好吧,我们编写了一个不同的验证代码,它只允许新结构或可能同时使用这两种结构,并使用 v2/api 和 v2/docs 根和文档 URI 制作新版本的 API。
所以我完全支持你的观点,我认为你提到的其他开发人员甚至不了解 OOP 和封装,更不用说 Web 服务和 REST API。