rest API 中验证答案的正确路径是什么?

What is the proper path in a rest API for verifying answers?

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

用户回答完后,答案应该 POSTed 到(或 GET from?)休息一下 API。

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

后端的模型只是简单的文档,每个文档由 subject id 和 40 个布尔值 as1as40.

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

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

我想我会像这样 POST body:

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

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

/statements/:id/answer?

您应该设计 API 端点(或 'paths'),使它们既真正具有描述性又特定于每个场景。所以考虑到这一点,端点之一可能看起来像:

/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`) 

What would be the proper way to call this rest service?

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

The backend just gets the correct answers from the DB and then verifies the users answers against those and responds with a result.

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

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

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

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

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

what should the path look like?

任何你喜欢的 - 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

还可以。


I guess I'm going with a POST body like so:

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

请求如

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

{...}

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

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


如果有效的只读语义不是您想要的,那么 it is okay to use POST。使用 HTML 表格,那将是我们唯一真正的选择。

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

将您的 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。