如何使用具有必填字段的 JSON 模式验证 http PATCH 数据

Posted

技术标签:

【中文标题】如何使用具有必填字段的 JSON 模式验证 http PATCH 数据【英文标题】:How to validate http PATCH data with a JSON schema having required fields 【发布时间】:2020-12-03 09:36:04 【问题描述】:

我正在尝试验证带有 必填 字段的经典 JSON 架构(带有 Ajv 和 json-server),但用于 HTTP PATCH 请求。

POST 没问题,因为数据是完整到达的,什么都不能省略。

但是,架构的必填字段在尝试修补现有资源时会出现问题。

这是我目前正在为 POST 做的事情:

const schema = require('./movieSchema.json');
const validate = new Ajv().compile(schema);

// ...

movieValidation.post('/:id', function (req, res, next) 
    const valid = validate(req.body);
    if (!valid) 
        const [err] = validate.errors;

        let field = (err.keyword === 'required') ? err.params.missingProperty : err.dataPath;

        return res.status(400).json(
            errorMessage: `Erreur de type '$err.keyword' sur le champs '$field' : '$err.message'`
        );
    
    next();
);

...但是如果我对movieValidation.patch(...) 做同样的事情并尝试只发送这块数据:


    "release_date": "2020-07-15",
    "categories": [
        "Action",
        "Aventure",
        "Science-Fiction",
        "Thriller"
    ]

...它将无法通过整个验证(而所有字段都可以并且它们验证架构)

这是我完整的moviesSchema.json


    "type": "object",
    "$schema": "http://json-schema.org/draft-07/schema#",
    "properties": 
        "title": 
            "title": "Titre",
            "type": "string",
            "description": "Titre complet du film"
        ,
        "release_date": 
            "title": "Date de sortie",
            "description": "Date de sortie du film au cinéma",
            "type": "string",
            "format": "date",
            "example": "2019-06-28"
        ,
        "categories": 
            "title": "Catégories",
            "description": "Catégories du film",
            "type": "array",
            "items": 
                "type": "string"
            
        ,
        "description": 
            "title": "Résumé",
            "description": "Résumé du film",
            "type": "string"
        ,
        "poster": 
            "title": "Affiche",
            "description": "Affiche officielle du film",
            "type": "string",
            "pattern": "^https?:\/\/"
        ,
        "backdrop": 
            "title": "Fond",
            "description": "Image de fond",
            "type": "string",
            "pattern": "^https?:\/\/"
        
    ,
    "required": [
        "title",
        "release_date",
        "categories",
        "description"
    ],
    "additionalProperties": false

现在,我使用与原始模式相同的不同模式来完成这个技巧,但最后没有 required 子句。但我不喜欢这种解决方案,因为它不必要地复制代码(而且一点也不优雅)。

是否有任何巧妙的解决方案/工具可以正确实现这一目标? 谢谢

【问题讨论】:

【参考方案1】:

如果您正确使用 HTTP PATCH,还有另一种方法可以解决此问题。

PATCH 请求的主体应该是某种差异媒体类型,而不是普通的 JSON。 diff 媒体类型定义了一组用于转换 JSON 的操作(添加、删除、替换)。然后将差异应用于原始资源,从而产生资源的新状态。几个 JSON diff 媒体类型是JSON Patch(更强大)和JSON Merge Patch(更自然)。

如果您验证 PATCH 的请求正文,您并没有真正验证您的资源,而是在验证 diff 格式。但是,如果您首先将补丁应用到您的资源,那么您可以使用完整架构验证结果(然后根据结果保留更改或 400)。

请记住,在 REST 中,重要的是资源和表示,而不是请求和响应。

【讨论】:

好的,我想我明白了。所以,我在上面尝试做的是一种“JSON 合并补丁”,但我的错误是试图在合并后验证差异而不是整个 JSON。我猜我最终可以使用json-merge-patch 包来实现这一点。我会试一试。感谢您分享 RFC,这真的很有启发性。【参考方案2】:

拥有多个架构并不少见,每个要验证的有效负载一个。

在你的情况下,看起来你做了完全正确的事情。

您可以使用引用 ($ref) 对架构进行重复数据删除,将您的属性子架构拆分为单独的文件。

您最终会得到一个包含您的模型的架构,以及该模型的每个表示的架构,但没有重复(在可能的情况下)。

如果您需要更多关于如何执行此操作的指导,请发表评论,我会更新答案并提供更多详细信息。


这是一个示例,说明您可以如何做您想做的事。 您将需要创建多个模式文件,并在需要验证 POST 或 PATCH 请求时引用正确的模式。 我已将示例简化为仅包含“标题”。

在一个模式中,您有类似...


  "$id": "https://example.com/object/movie",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": 
    "movie": 
      "properties": 
        "title": 
          "$ref": "#/definitions/title"
        
      ,
      "additionalProperties": false
    ,
    "title": 
      "title": "Titre",
      "type": "string",
      "description": "Titre complet du film"
    
  

然后你会有一个用于 POST 和 PATCH...


  "$id": "https://example.com/movie/patch",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "allOf": [
    "$ref": "/object/movie#/definitions/movie"
  ],


  "$id": "https://example.com/movie/post",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "allOf": [
    "$ref": "/object/movie#/definitions/movie"
  ],
  "required": ["title"]

example.com 更改为您想用作ID 的任何域。 然后,您需要将所有架构加载到实现中。

一旦加载,引用就会起作用,因为它们基于 URI 解析,对每个架构资源使用 $id

请注意 POST 和 PATCH 架构中的 $ref 值不以 # 开头,这意味着目标不是此架构资源。

【讨论】:

我最近发现了 JSON 模式,但我还不知道可以用它做什么。你会如何使用$ref 来做moviesSchema.json 的子模式?欢迎举例?谢谢 @jmpp,看看这个链接:json-schema.org/understanding-json-schema/… 链接很好,但如果链接消失,则无用。 另外,链接中的示例不显示交叉文件引用

以上是关于如何使用具有必填字段的 JSON 模式验证 http PATCH 数据的主要内容,如果未能解决你的问题,请参考以下文章

使用JSON模式验证protobuf消息?

如何在javascript中验证之前修剪非必填字段

如何防止提交但仍验证必填字段?

jquery.validation - 验证必填字段时如何忽略默认值

如何验证复合控件

如何使用可重复对象中的嵌套必填字段将数据从一个表复制到另一个表