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

时间:2018-01-08 16:06:56

标签: jsonschema

我的问题与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关键字)

我认为正确的方法是将requiredsubschemasboolean logic合并,但我不确定如何将它们联系在一起。

这是正确的道路,还是有更好的解决方案?有没有人有一个适用于最新版本的JSON Schema的例子(目前草案7)

谢谢!

1 个答案:

答案 0 :(得分:2)

这里有几种可能的方法。

首先,readOnly永远不会导致JSON Schema的验证过程失败。它不是一个断言,只是一个应用程序可以用来随意采取行动的注释。因此,您的服务器应用程序可以处理它,但是如果有人试图更改它,或者只是默默地忽略新值,则会引发错误。

或者查看HTTP Prefer标头,如果存在handling=lenient,则忽略任何更改只读值的尝试,但如果存在handling=strict,则任何尝试都会出错。

或者以不同方式处理不同的字段。如果您有lastModified字段并支持HTTP GET => modify => HTTP PUT工作流程,然后当您返回表示时,lastModified将不可避免地出错,但在某些时候是只读的。您不希望破坏PUT的过期自动生成时间戳。但是,如果有人认为他们可以更改id字段,您可能确实想提出错误。

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

"双向工作"方法会从id中删除required,但请注明,一旦创建,资源将始终具有id。但是这样可以在创建或写入时省略它而不会出现问题。要确定这是否合理,请考虑客户端输出验证的用例。客户端是否真的需要JSON Schema来检查服务器是否发送了id,还是客户端可以根据文档合理地假设服务器能够正确执行的操作?

对于"锁定每个方式"方法,你可以做这样的事情:

{
    "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本身永远不会导致验证失败。这是我更喜欢为读取和写入都有一个模式的一个原因,让应用程序处理差异(并记录该处理的工作方式)。