英文:
REST API - Best practice for "exists in"
问题
好的,以下是翻译的内容:
假设我有一个教室,教室里有很多学生。
我想要提供一个API,以便针对每个学生都能列出他所在的教室(学生可以在多个教室上课)。
显而易见的API暴露方式是这样的:
GET https://my.app/api/students/{student_id}/classrooms
然而 - 由于教室在我的数据库中保存了学生 我必须访问每个教室并检查其中的学生,这可能需要很长时间(我可能有数百万个教室)
鉴于在我的用例中,用户应该已经熟悉教室,我想提供一个“存在于”API。(当然也要限制列表)
我想过两种选项,但都有点违反直觉。
选项1
GET https://my.app/api/students/{student_id}/classrooms?classrooms=1,2,3...
选项2
GET https://my.app/api/students/{student_id}/classrooms
BODY- {"classrooms": [1,2,3...]}
这两个选项中,哪一个更直观/更有意义,或者是否还有其他选项?
英文:
Let's say I have a classroom, and the classroom holds many students.
I would like to expose an API saying for each student what are his classrooms (student can have multiple classrooms).
The obvious API to expose is something like this:
GET https://my.app/api/students/{student_id}/classrooms
However - Since the classroom holds the students on my DB I'll have to go to each classroom and check for it's students which could take a long time (I could have millions of classrooms)
Since In my use case the user should already be familiar with the classrooms I would like to expose an "exists in" API. (and of course limit that list)
I've thought about two options and both kinda look counter-intuitive.
<h3>Option 1 </h3>
GET https://my.app/api/students/{student_id}/classrooms?classrooms=1,2,3...
<h3>Option 2 </h3>
GET https://my.app/api/students/{student_id}/classrooms
BODY- {"classrooms": [1,2,3...]}
Does any of the options is intuitive/make sense or alternatively is there any other option?
答案1
得分: 2
GET https://my.app/api/students/{student_id}/classrooms
{ "classrooms": [1, 2, 3, ...] }
超出边界 - 如果在代码审查中立即拒绝,无需进一步阅读。问题在于这与RFC 7231定义的GET的语义不一致。
> GET请求消息中的载荷没有定义的语义;在GET请求上发送载荷主体可能会导致一些现有实现拒绝请求。
特别是 - 通过将资源的标识信息从URI移动到消息主体中,实际上是将该信息从通用缓存中隐藏起来。您需要巨大的补偿效益来使这种权衡变得有价值,而我在这里没有看到这种补偿。
> 协议确实支持带有主体的GET
是的,但并没有以有用的方式。您绝对正确,协议允许带有主体的GET,参见RFC 7230。
> 请求消息的框架与方法语义无关,即使该方法不定义主体的任何用途。
但在GET的情况下,主体没有实际意义;就通用组件而言,GET请求的主体只是噪音。
GET https://my.app/api/students/{student_id}/classrooms?classrooms=1,2,3...
更好。在这里,您遵守了GET的语义,缓存具有正确存储和获取表示所需的所有信息。
在资源设计中需要考虑的一件事是:您是想为每种可能的教室号码组合创建不同的文档,还是想创建一些可重复使用的较少数量的文档,这些文档可以与不同的房间组合一起使用。
这是一个权衡:粗粒度资源可以更好地利用缓存来进行读取,但也需要客户端代码更多的工作。
尽管如此:
> 由于教室保存在我的数据库中的学生,我必须进入每个教室并检查它的学生,这可能需要很长时间
如果这是一个重要的问题,那么也许您应该设计您的数据模型以更有机地支持它(例如能够直接按学生ID获取教室)。
<details>
<summary>英文:</summary>
GET https://my.app/api/students/{student_id}/classrooms
{"classrooms": [1,2,3...]}
Out of bounds - I would immediately reject in a code review without reading further. The problem here is that it is inconsistent with the _semantics_ of GET as defined by [RFC 7231][1]
> A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.
In particular - by taking identifying information of the resource and moving it from the URI to the message body, you are, in effect, hiding that information from general purpose caches. You would need an enormous compensating benefit to make that tradeoff worth while, and I don't see that compensation here.
> the protocol does support GET with body
Yes, but not in a useful way. You are absolutely correct that the protocol permits GET with a body, see RFC 7230.
> Request message framing is independent of method semantics, even if the method does not define any use for a message body.
But in the case of GET, the body doesn't _mean_ anything; as far as a general purpose component is concerned, the body of a GET request is just noise.
GET https://my.app/api/students/{student_id}/classrooms?classrooms=1,2,3...
Better. Here, you are compliant with the semantics of GET, the cache has all of the information needed to correctly store and fetch representations.
One thing to consider in your resource design: do you want to have a different document for every possible combination of classroom numbers, or do you want to have some smaller number of documents that are re-usable with different combinations of rooms.
It's a trade off: coarse grained resources allow your reads to better leverage caching, but also requires more work by client code.
That said:
> Since the classroom holds the students on my DB I'll have to go to each classroom and check for it's students which could take a long time
If this is an important problem, then perhaps you should design your data model to support it more organically (like being able to fetch classrooms by student id directly).
[1]: https://www.rfc-editor.org/rfc/rfc7231#section-4.3.1
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论