如何在单个 JSON 模式上验证输入和输出(只读的边缘情况)

How to validate input and output on a single JSON Schema (edge cases with readOnly)

我的问题与 JSON schema for data description vs data validation vs input validation 有一些重叠,但差异很大,我觉得需要提出一个新问题。

我正在尝试了解设计一种模式的最佳方法,该模式可用于验证单个资源表示的输入和输出。这是一个例子。

{
    "type": "object",
    "properties": {
        "id": {
            "type": "number",
            "readOnly": true
        },
        "title": {
            "type": "string"
        },
        "post": {
            "type": "string"
        }
    },
    "required": ["id", "title", "post"],
    "additionalProperties": false
};

问题是在验证输出时,我们需要 idtitlepost。验证输入时,将不需要一个或多个字段,如果它们提供 id 我希望验证失败(由于 readOnly 关键字)

我认为正确的方法是将 requiredsubschemas or boolean logic 结合使用,但我不确定如何将它们结合在一起。

这是正确的路径,还是有更好的解决方案?有谁有适用于最新版本 JSON Schema(此时草案 7)

的示例

谢谢!

这里有几种可能的方法。

首先,readOnly 永远不会导致 JSON 架构的验证过程失败。这不是一个断言,只是一个注解,应用程序可以根据需要使用它来采取行动。因此,您的服务器应用程序可以随心所欲地处理它 - 如果有人试图更改它,则会引发错误,或者只是默默地忽略新值。

或者查看 HTTP Prefer header,如果存在 handling=lenient,请忽略任何更改 read-only 值的尝试,但如果存在 handling=strict存在,任何尝试都会出错。

或者以不同的方式处理不同的字段。如果你有一个 lastModified 字段并支持 HTTP GET => modify => HTTP PUT 工作流,那么当你 PUT 一个表示时, lastModified 将不可避免地出错但 read-only 在某些时候.您不想因为过时的 auto-generated 时间戳而中断 PUT。但是如果有人认为他们可以更改 id 字段,您可能确实想提出错误。

鉴于此,有两种通用方法:编写一个双向工作的模式并在您的应用程序层中进行额外检查,或者分别确定输入和输出的所有内容。

"works both ways" 方法会从 required 中删除 id,但请注意,一旦创建,资源将始终具有 id。但是这样它可以在创建或写入时被省略而不会出现问题。要确定这是否合理,请考虑 client-side 输出验证的用例。客户端是否真的需要 JSON 架构来检查服务器是否发送了 id,或者客户端是否可以根据文档合理地假设服务器会正​​确执行?

对于 "lock down each way" 方法,您可以这样做:

{
    "definitions": {
        "common": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "number",
                    "readOnly": true
                },
                "title": {
                    "type": "string"
                },
                "post": {
                    "type": "string"
                }
            },
            "required": ["title", "post"],
            "additionalProperties": false
        },
        "input": {
            "allOf": [
                {"$ref": "#/definitions/common"},
                {"properties": {"id": false}}
            ]
        },
        "output": {
            "allOf": [
                {"$ref": "#/definitions/common"},
                {"required": ["id"]}
            ]
        }
    }
}

您需要在公共模式中定义 id 以便 "additionalProperties": false 知道它(我尽量避免 "additionalProperties": false 因为它使得以兼容的方式演化表示变得困难,但是如果你想使用它,这是让它工作的方法。

对于输出,只需要id

对于输入,使用{"properties": {"id": false}}禁用id。即使它是在 "common" 中定义的,false 布尔模式总是会导致 id 无法通过输入验证。

当然,您必须弄清楚您的应用程序如何知道要使用哪个模式,因为再次 readOnly 其自身永远不会导致验证失败。这是我更喜欢为读取和写入使用一个模式并让应用程序处理差异(并记录该处理如何工作)的原因之一。