# 严格模式

¥Strict mode

严格结构旨在防止用户结构中出现任何意外行为或默默忽略的错误。与规范相比,它不会更改任何校验结果,但它会使某些结构无效,并在违反任何限制的情况下引发异常或日志警告(使用 strict: "log" 选项)。

¥Strict mode intends to prevent any unexpected behaviours or silently ignored mistakes in user schemas. It does not change any validation results compared with the specification, but it makes some schemas invalid and throws exception or logs warning (with strict: "log" option) in case any restriction is violated.

要禁用所有严格结构限制,请使用选项 strict: false。一些限制可以通过自己的选项进行更改

¥To disable all strict mode restrictions use option strict: false. Some of the restrictions can be changed with their own options

# JSON 类型定义结构

¥JSON Type Definition schemas

JTD specification is strict - 无论是否启用 Ajv 严格模式,它都不允许具有被忽略或不明确元素的模式,包括:

¥JTD specification is strict - whether Ajv strict mode is enabled or not it will not allow schemas with ignored or ambiguous elements, including:

  • 未知结构关键字

    ¥unknown schema keywords

  • 将多种结构形式合并到一个结构中

    ¥combining multiple schema forms in one schema

  • 将相同的属性定义为必需属性和可选属性

    ¥defining the same property as both required and optional

  • 重新定义属性内的鉴别器标记,即使定义不矛盾

    ¥re-defining discriminator tag inside properties, even if the definition is non-contradictory

非正式规范描述参见 JSON 类型定义,正式规范描述参见 RFC8927 (opens new window)

¥See JSON Type Definition for informal and RFC8927 (opens new window) for formal specification descriptions.

严格结构对 JTD 结构引入的唯一更改(不更改其语法或语义)是要求可选 metadata 成员中出现的所有成员都定义为 Ajv 关键字。可以使用 strict: false 选项禁用此限制,而不会对其他 JTD 功能产生任何影响。

¥The only change that strict mode introduces to JTD schemas, without changing their syntax or semantics, is the requirement that all members that are present in optional metadata members are defined as Ajv keywords. This restriction can be disabled with strict: false option, without any impact to other JTD features.

# JSON Schema 结构

¥JSON Schema schemas

JSON 结构规范非常宽松,允许结构中的许多元素被悄悄忽略或不明确。建议使用严格结构的 JSON Schema。

¥JSON Schema specification is very permissive and allows many elements in the schema to be quietly ignored or be ambiguous. It is recommended to use JSON Schema with strict mode.

值为 true/false/"log" 的选项 strict 可用于将所有严格结构限制设置为相同,否则可以使用单独的严格结构选项。参见 严格模式选项

¥Option strict with value true/false/"log" can be used to set all strict mode restriction to be the same, otherwise individual strict mode options can be used. See Strict mode options.

# 禁止忽略关键字

¥Prohibit ignored keywords

strictSchema option

# 未知关键字

¥Unknown keywords

JSON Schema 第 6.5 节 (opens new window) 要求忽略未知关键字。动机是提高结构的跨平台可移植性,以便不支持某些关键字的实现仍然可以进行部分校验。

¥JSON Schema section 6.5 (opens new window) requires to ignore unknown keywords. The motivation is to increase cross-platform portability of schemas, so that implementations that do not support certain keywords can still do partial validation.

这种方法的问题是:

¥The problems with this approach are:

  • 相同结构和数据的不同校验结果会导致错误和不一致的行为。

    ¥Different validation results with the same schema and data, leading to bugs and inconsistent behaviours.

  • 关键字中的拼写错误会导致关键字被悄悄忽略,需要对结构进行广泛的测试覆盖范围以避免这些错误。

    ¥Typos in keywords resulting in keywords being quietly ignored, requiring extensive test coverage of schemas to avoid these mistakes.

默认情况下,当使用未知关键字时,Ajv 会失败结构编译。用户可以明确定义应该允许和忽略的关键字:

¥By default Ajv fails schema compilation when unknown keywords are used. Users can explicitly define the keywords that should be allowed and ignored:

ajv.addKeyword("allowedKeyword")

或对多个关键字使用便捷方法 addVocabulary

¥or use the convenience method addVocabulary for multiple keywords

ajv.addVocabulary(["allowed1", "allowed2"]) // simply calls addKeyword multiple times

# 忽略 "additionalItems" 关键字

¥Ignored "additionalItems" keyword

如果 "items" 关键字不存在或者不是项目数组,则 JSON 结构部分 9.3.1.2 (opens new window) 要求忽略 "additionalItems" 关键字。这与 "additionalProperties" 和 "properties" 的相互作用不一致,可能会导致意外结果。

¥JSON Schema section 9.3.1.2 (opens new window) requires to ignore "additionalItems" keyword if "items" keyword is absent or if it is not an array of items. This is inconsistent with the interaction of "additionalProperties" and "properties", and may cause unexpected results.

默认情况下,当 "additionalItems" 不带 "items" 使用时(或者 "items" 不是数组),Ajv 会失败结构编译。

¥By default Ajv fails schema compilation when "additionalItems" is used without "items" (or if "items" is not an array).

# 忽略 "if"、"then"、"else" 关键字

¥Ignored "if", "then", "else" keywords

如果 "then" 和 "else" 都不存在,则 JSON 结构部分 9.2.2 (opens new window) 要求忽略 "if"(仅收集注释);如果不存在 "if",则忽略 "then"/"else"。

¥JSON Schema section 9.2.2 (opens new window) requires to ignore "if" (only annotations are collected) if both "then" and "else" are absent, and ignore "then"/"else" if "if" is absent.

默认情况下,Ajv 在这些情况下会失败结构编译。

¥By default Ajv fails schema compilation in these cases.

# 忽略 "contains"、"maxContains" 和 "minContains" 关键字

¥Ignored "contains", "maxContains" and "minContains" keywords

如果缺少 "contains" 关键字,JSON 结构部分 6.4.4, 6.4.5 (opens new window) 需要忽略关键字 "maxContains" 和 "minContains"。

¥JSON Schema sections 6.4.4, 6.4.5 (opens new window) require to ignore keywords "maxContains" and "minContains" if "contains" keyword is absent.

这也意味着当 "minContains" 为 0 并且 "maxContains" 不存在时,"contains" 关键字始终有效。

¥It is also implied that when "minContains" is 0 and "maxContains" is absent, "contains" keyword is always valid.

默认情况下,Ajv 在这些情况下会失败结构编译。

¥By default Ajv fails schema compilation in these cases.

# 未知格式

¥Unknown formats

默认情况下,未知格式会在结构编译期间引发异常(如果格式关键字值为 $data 参考,则校验失败)。可以使用选项 validateFormats: false 完全退出格式校验。你可以使用 addFormat 方法或 formats 选项定义所有已知格式 - 要忽略某些格式,请传递 true 作为其定义:

¥By default unknown formats throw exception during schema compilation (and fail validation in case format keyword value is $data reference). It is possible to opt out of format validation completely with options validateFormats: false. You can define all known formats with addFormat method or formats option - to have some format ignored pass true as its definition:

const ajv = new Ajv({formats: {
  reserved: true
})

ajv-formats (opens new window) 包中提供了标准 JSON Schema 格式 - see Formats section.

¥Standard JSON Schema formats are provided in ajv-formats (opens new window) package - see Formats section.

# 忽略默认值

¥Ignored defaults

使用 useDefaults 选项时,Ajv 通过分配结构中的默认值来修改已校验的数据,但是当可以忽略默认值时,存在不同的限制(请参阅 分配默认值)。在严格结构下,如果结构中使用了此类默认值,则 Ajv 会失败结构编译。

¥With useDefaults option Ajv modifies validated data by assigning defaults from the schema, but there are different limitations when the defaults can be ignored (see Assigning defaults). In strict mode Ajv fails schema compilation if such defaults are used in the schema.

# 防止意外校验

¥Prevent unexpected validation

# "properties" 和 "patternProperties" 关键字重叠

¥Overlap between "properties" and "patternProperties" keywords

allowMatchingProperties option

用户的期望(参见#196、#286)是 "patternProperties" 仅适用于 "properties" 关键字中尚未定义的属性,但 JSON Schema 部分 9.3.2 (opens new window) 将这两个关键字定义为独立的。这意味着对于某些属性可以应用两个子模式 - 一个在 "properties" 关键字中定义,另一个在 "patternProperties" 中定义,用于匹配此属性的模式。

¥The expectation of users (see #196, #286) is that "patternProperties" only apply to properties not already defined in "properties" keyword, but JSON Schema section 9.3.2 (opens new window) defines these two keywords as independent. It means that to some properties two subschemas can be applied - one defined in "properties" keyword and another defined in "patternProperties" for the pattern matching this property.

默认情况下,如果 "patternProperties" 中的结构与同一结构中 "properties" 中的属性匹配,则 Ajv 会失败结构编译。

¥By default Ajv fails schema compilation if a pattern in "patternProperties" matches a property in "properties" in the same schema.

除了使用选项 strict: falsestrictSchema: false 允许此类模式外,还有一个选项 allowMatchingProperties: true 仅允许这种情况而不禁用其他严格模式限制 - 在极少数情况下,这是必要的。

¥In addition to allowing such patterns by using option strict: false or strictSchema: false, there is an option allowMatchingProperties: true to only allow this case without disabling other strict mode restrictions - there are some rare cases when this is necessary.

重申一下,这个或其他严格模式限制都不会改变校验结果 - 它们仅限制哪些模式有效。

¥To reiterate, neither this nor other strict mode restrictions change the validation results - they only restrict which schemas are valid.

# 定义所需的属性

¥Defined required properties

strictRequired option

将选项 strictRequired 设置为 "log"true 时,如果 "required" 关键字中使用的属性未在与同一对象(数据实例)相关的相同或某个父结构中的 "properties" 关键字中定义,Ajv 会记录警告或引发异常。

¥With option strictRequired set to "log" or true Ajv logs warning or throws exception if the property used in "required" keyword is not defined in "properties" keyword in the same or some parent schema relating to the same object (data instance).

默认情况下此选项被禁用。

¥By default this option is disabled.

父结构中定义的属性

在某些情况下,不会考虑父结构中定义的属性。

¥There are cases when property defined in the parent schema will not be taken into account.

# 无约束元组

¥Unconstrained tuples

strictTuples option

如果 "items" 是一个数组(对于定义元组的结构),但 "minItems" 和 "additionalItems"/"maxItems" 关键字都不存在(或具有错误的值),Ajv 还会记录警告:

¥Ajv also logs a warning if "items" is an array (for schema that defines a tuple) but neither "minItems" nor "additionalItems"/"maxItems" keyword is present (or have a wrong value):

{
  type: "array",
  items: [{type: "number"}, {type: "boolean"}]
}

上面的结构可能有错误,因为元组通常应该具有固定的大小。对 "fix" 来说:

¥The above schema may have a mistake, as tuples usually are expected to have a fixed size. To "fix" it:

{
  type: "array",
  items: [{type: "number"}, {type: "boolean"}],
  minItems: 2,
  additionalItems: false
  // or
  // maxItems: 2
}

有时,用户会意外地为单元(包含一个项目的元组)创建仅校验第一项的结构,此限制也可以防止这种错误。

¥Sometimes users accidentally create schema for unit (a tuple with one item) that only validates the first item, this restriction prevents this mistake as well.

使用 strictTuples 选项抑制此警告 (false) 或将其转变为异常 (true)。

¥Use strictTuples option to suppress this warning (false) or turn it into exception (true).

如果你使用 JSONSchemaType<T>,这个错误也将在类型级别上被防止。

¥If you use JSONSchemaType<T> this mistake will also be prevented on a type level.

# 严格类型

¥Strict types

strictTypes option

附加选项 strictTypes(默认为 "log")对 type 关键字的使用方式施加了附加限制:

¥An additional option strictTypes ("log" by default) imposes additional restrictions on how type keyword is used:

# 联合类型

¥Union types

allowUnionTypes option

使用 strictTypes 选项,禁止使用多种类型的 "type" 关键字("null" 除外)。

¥With strictTypes option "type" keywords with multiple types (other than with "null") are prohibited.

无效的:

¥Invalid:

{
  type: ["string", "number"]
}

有效的:

¥Valid:

{
  type: ["object", "null"]
}

and

{
  type: "object",
  nullable: true
}

联合仍然可以使用 anyOf 关键字定义。

¥Unions can still be defined with anyOf keyword.

此限制的动机是 "type" 通常不是结构中的唯一关键字,并且混合适用于不同类型的其他关键字会令人困惑。它还与更广泛版本的 OpenAPI 规范一致,并具有更好的工具支持。例如,这个例子违反了 strictTypes

¥The motivation for this restriction is that "type" is usually not the only keyword in the schema, and mixing other keywords that apply to different types is confusing. It is also consistent with wider range of versions of OpenAPI specification and has better tooling support. E.g., this example violating strictTypes:

{
  type: ["number", "array"],
  minimum: 0,
  items: {
    type: "number",
    minimum: 0
  }
}

相当于这个符合标准的例子,它更冗长但也更容易维护:

¥is equivalent to this complying example, that is more verbose but also easier to maintain:

{
  anyOf: [
    {
      type: "number",
      minimum: 0,
    },
    {
      type: "array",
      items: {
        type: "number",
        minimum: 0,
      },
    },
  ]
}

也可以重构:

¥It also can be refactored:

{
  $defs: {
    item: {
      type: "number",
      minimum: 0
    }
  },
  anyOf: [
    {$ref: "#/$defs/item"},
    {
      type: "array",
      items: {$ref: "#/$defs/item"}
    },
  ]
}

此限制可以通过 allowUnionTypes: true 选项与其他 strictTypes 限制分开解除。

¥This restriction can be lifted separately from other strictTypes restrictions with allowUnionTypes: true option.

# 矛盾类型

¥Contradictory types

子模式可以应用于相同的数据实例,并且可能具有矛盾的类型关键字 - 它通常表明存在一些错误。例如:

¥Subschemas can apply to the same data instance, and it is possible to have contradictory type keywords - it usually indicate some mistake. For example:

{
  type: "object",
  anyOf: [
    {type: "array"},
    {type: "object"}
  ]
}

上面的结构违反了 strictTypes,因为 "array" 类型与对象不兼容。如果你使用 allowUnionTypes: true 选项,则可以通过以下方式修复上述结构:

¥The schema above violates strictTypes as "array" type is not compatible with object. If you used allowUnionTypes: true option, the above schema can be fixed in this way:

{
  type: ["array", "object"],
  anyOf: [
    {type: "array"},
    {type: "object"}
  ]
}

"number" 与 "integer"

类型 "number" 可以缩小为 "integer",反之则违反 strictTypes

¥Type "number" can be narrowed to "integer", the opposite would violate strictTypes.

# 需要适用类型

¥Require applicable types

这个简单的 JSON Schema 是有效的,但它违反了 strictTypes

¥This simple JSON Schema is valid, but it violates strictTypes:

{
  properties: {
    foo: {type: "number"},
    bar: {type: "string"}
  }
  required: ["foo", "bar"]
}

这是一个非常常见的错误,即使是有 JSON Schema 经验的人也经常犯这种错误 - 这里的问题是任何不是对象的值对于这个模式都是有效的 - this is rarely intentional.

¥This is a very common mistake that even people experienced with JSON Schema often make - the problem here is that any value that is not an object would be valid against this schema - this is rarely intentional.

要修复它,必须添加 "type" 关键字:

¥To fix it, "type" keyword has to be added:

{
  type: "object",
  properties: {
    foo: {type: "number"},
    bar: {type: "string"}
  },
  required: ["foo", "bar"]
}

你不一定必须在同一结构对象中具有 "type" 关键字;只要有 "type" 关键字应用于同一个 schema 文档中数据实例的相同部分,而不是通过 "$ref",就可以了:

¥You do not necessarily have to have "type" keyword in the same schema object; as long as there is "type" keyword applying to the same part of data instance in the same schema document, not via "$ref", it will be ok:

{
  type: "object",
  anyOf: [
    {
      properties: {foo: {type: "number"}}
      required: ["foo"]
    },
    {
      properties: {bar: {type: "string"}}
      required: ["bar"]
    }
  ]
}

"properties" 和 "required" 都需要 type: "object" 来满足 strictTypes - 在父模式中拥有一次就足够了,而无需在每个模式中重复它。

¥Both "properties" and "required" need type: "object" to satisfy strictTypes - it is sufficient to have it once in the parent schema, without repeating it in each schema.

# 严格的数字校验

¥Strict number validation

strictNumbers option

严格结构也会影响数字校验。默认情况下,Ajv 无法通过 InfinityNaN{"type": "number"}(或 "integer")校验。

¥Strict mode also affects number validation. By default Ajv fails {"type": "number"} (or "integer") validation for Infinity and NaN.