-1

我正在 MERN 堆栈中建立一个用于教育目的的网站。它有一个针对一个特定主题的问卷,其中包含 40 个“是或否”问题(可以是真或假的陈述)。可以有许多主题,但所有主题的陈述总是相同的。

用户完成回答后,应将答案发布到(或从?)休息 API。

后端只是从数据库中获取正确的答案,然后根据这些验证用户的答案并以结果进行响应。

后端的模型只是简单的文档,每个文档由subject id40 个布尔值as1组成as40

(对 nosql 来说是新手,所以我可能已经完全破坏了我的模型,在那种情况下,我很乐意接受纠正!)

调用此休息服务的正确方法是什么?

我想我要使用这样的 POST 正文:

{
    "answers": {
        "uas1": true,
        "uas2": false,
        ...
        "uas39": true,
        "uas40": false
    }
}

...但是路径应该是什么样子?

/statements/:id/answer?

4

2 回答 2

1

调用此休息服务的正确方法是什么?

您需要解决的主要问题是了解客户端是在获取资源(有效的只读操作)还是在修改资源。获取资源可能意味着将资源从缓存中取出,而不是让请求一直到达您的服务器。

后端只是从数据库中获取正确的答案,然后根据这些验证用户的答案并以结果进行响应。

这听起来很像有效的只读案例:给定答案列表,找到匹配的答案文档。因此,这种资源模型的通常答案是使用 GET。

在网络上,这可能看起来像一个带有 40 个不同问题的输入控件的大表单。用户会做出他们的选择,然后点击提交按钮。浏览器将使用输入来计算查询字符串,并执行 HTTP GET 请求,并将所有用户输入编码到查询部分(根据表单本身的元数据。

?a1=Y&a2=N&a3=T...&a40=Y

使用 HTML 表单,我们可以为我们想要的路径使用任何拼写,同样,对于查询部分中的键,我们可以使用任何我们想要的拼写,因为该信息成为表单元数据的一部分:浏览器可以只查看表单定义,并描述了如何创建有效的请求 URI(它是 HTML 表单处理标准的一部分)。

对于诸如主题 ID 之类的内容,您可以选择将该信息编码到路径中(通过使该信息成为表单操作的一部分)或查询部分(作为表单输入 - 可能“隐藏”)。

路径应该是什么样子?

你喜欢的任何东西 - REST 不关心你为资源标识符使用的拼写约定。

GET /83eeecdd-a680-475f-913b-07aa0239cec0?a1=Y&a2=N&a3=T...&a40=Y

……很好。不过,您可能想要对人类更好的东西——阅读日志的操作员、扫描浏览器历史记录的用户、试图为其他开发人员记录资源模型的作者等等。

GET /subject/:subject_id/answers?a1=Y&a2=N&a3=T...&a40=Y

也很好。


我想我要使用这样的 POST 正文:

根据您的描述,不是我的首选。

像这样的请求

POST /statements/:id/answer
Content-Type: application/json

{...}

从通用组件的角度来看,约束很少;通用组件不能假定 POST 是有效只读的。这意味着我们无法从缓存中提取答案。事实上,情况正好相反:一个成功的 POST 请求将导致通用缓存使用相同的有效 uri 使以前存储的响应无效。

通用组件甚至不能假设 POST 请求具有幂等语义,因此如果响应丢失,HTTP 应用程序无法通过重新发送请求来自动帮助我们。


如果您不需要有效的只读语义,则可以使用 POST。对于 HTML 表单,这将是我们唯一真正的选择。

但是,如果您可以设计您的资源模型,使得每组提交的答案都是它自己的资源,那就更好了。你可能会这样想:用户得到一份独特的问卷,其中所有的答案都是空白的。他们填写自己的答案,并返回该独特问卷的新表示。

将您的 API 实现为对文档的编辑允许您使用 PUT;PUT 具有幂等语义,这意味着当响应在不可靠的网络上丢失时,通用组件可以自动提供帮助。

PUT /statements/123/answers/Bob
Content-Type: application/json

{
    "answers": {
        "uas1": true,
        "uas2": false,
        ...
        "uas39": true,
        "uas40": false
    }
}

在这里,我们有一个特定于 Bob 答案的资源,并且请求描述了对该资源的编辑。

从技术上讲,您可以让每个人都编辑相同的资源

PUT /statements/123/answers
...

这不是很好,但你会得到很多好处。每个成功提交的请求都会使目标资源失效,但您可能不太在意。

我不会将此方法称为 REST 兼容的,但它可能算作 REST-close-enough-that-you-get-away-with-it。

于 2021-04-12T02:16:57.563 回答
0

您应该设计您的 API 端点(或“路径”),使其既能真正具有描述性又能针对每个场景。所以考虑到这一点,其中一个端点可能看起来像:

/subject/:subject_id/answers

然后在您的前端,您将 POST 请求发送到例如

http://127.0.0.1/3000/subject/23/answers

请求正文如下所示:

{
    "answers": {
        "uas1": true,
        "uas2": false,
        ...
        "uas39": true,
        "uas40": false
    }
}

这个端点本质上会做的是它将 id 为 23 的主题的所有 40 个布尔答案发送到您的 Express.js 服务器。

然后您的服务器将解析请求并从您的 req.body 中获取答案,然后根据您的 MongoDB 数据库检查它们。该检查的结果然后可以在这样的响应中发回:

res.status(200).send(`For subject ${subject_name}, you scored ${correct_answers}/40`) 
于 2021-04-11T21:08:56.323 回答