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

问题描述

我正在尝试使用必填字段验证经典的JSON模式(带有Ajvjson-server),但用于HTTP PATCH请求。

对于POST来说是可以的,因为数据已全部到达并且不应遗漏任何内容

但是,尝试修补现有资源时,架构的 required 字段会引起问题。

这是我目前正在做的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","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","pattern": "^https?:\/\/"
        },"backdrop": {
            "title": "Fond","description": "Image de fond","pattern": "^https?:\/\/"
        }
    },"required": [
        "title","release_date","categories","description"
    ],"additionalProperties": false
}

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

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

解决方法

具有多个模式(要验证的每个有效负载一个)并不罕见。

根据您的情况,您似乎做对了正确的事情。

您可以使用引用($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","allOf": [{
    "$ref": "/object/movie#/definitions/movie"
  }],}
{
  "$id": "https://example.com/movie/post","required": ["title"]
}

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

加载后,引用将基于URI解析而起作用,并为每个架构资源使用$id

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

,

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

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

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

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