Skip to main content

验证和序列化

验证和序列化

¥Validation and Serialization

Fastify 使用基于模式的方法,即使它不是强制性的,我们也建议使用 JSON 结构 来验证你的路由并序列化你的输出。在内部,Fastify 将模式编译为高性能函数。

¥Fastify uses a schema-based approach, and even if it is not mandatory we recommend using JSON Schema to validate your routes and serialize your outputs. Internally, Fastify compiles the schema into a highly performant function.

仅当内容类型为 application-json 时才会尝试验证,如 内容类型解析器 的文档中所述。

¥Validation will only be attempted if the content type is application-json, as described in the documentation for the content type parser.

本节中的所有示例均使用 JSON 架构草案 7 规范。

¥All the examples in this section are using the JSON Schema Draft 7 specification.

⚠ 安全须知

¥⚠ Security Notice

将架构定义视为应用代码。验证和序列化功能使用 new Function() 动态评估代码,这对于与用户提供的架构一起使用是不安全的。请参阅 Ajvfast-json-stringify 了解更多详情。

¥Treat the schema definition as application code. Validation and serialization features dynamically evaluate code with new Function(), which is not safe to use with user-provided schemas. See Ajv and fast-json-stringify for more details.

无论 Fastify 是否支持 $async Ajv 功能,都不应将其用作第一个验证策略的一部分。此选项用于访问数据库,在验证过程中读取数据库可能会导致你的应用遭受拒绝服务攻击。如果你需要运行 async 任务,请在验证完成后改用 Fastify 的钩子,例如 preHandler

¥Regardless the $async Ajv feature is supported by Fastify, it should not be used as part of the first validation strategy. This option is used to access Databases and reading them during the validation process may lead to Denial of Service Attacks to your application. If you need to run async tasks, use Fastify's hooks instead after validation completes, such as preHandler.

核心理念

¥Core concepts

验证和序列化任务由两个不同的、可定制的参与者处理:

¥The validation and the serialization tasks are processed by two different, and customizable, actors:

这两个独立的实体仅共享通过 .addSchema(schema) 添加到 Fastify 实例的 JSON 模式。

¥These two separate entities share only the JSON schemas added to Fastify's instance through .addSchema(schema).

添加共享架构

¥Adding a shared schema

借助 addSchema API,你可以向 Fastify 实例添加多个模式,然后在应用的多个部分中重用它们。像往常一样,这个 API 是被封装的。

¥Thanks to the addSchema API, you can add multiple schemas to the Fastify instance and then reuse them in multiple parts of your application. As usual, this API is encapsulated.

共享架构可以通过 JSON Schema $ref 关键字重用。以下是参考文献工作原理的概述:

¥The shared schemas can be reused through the JSON Schema $ref keyword. Here is an overview of how references work:

  • myField: { $ref: '#foo'} 将在当前模式中搜索具有 $id: '#foo' 的字段

    ¥myField: { $ref: '#foo'} will search for field with $id: '#foo' inside the current schema

  • myField: { $ref: '#/definitions/foo'} 将在当前模式中搜索字段 definitions.foo

    ¥myField: { $ref: '#/definitions/foo'} will search for field definitions.foo inside the current schema

  • myField: { $ref: 'http://url.com/sh.json#'} 将搜索使用 $id: 'http://url.com/sh.json' 添加的共享模式

    ¥myField: { $ref: 'http://url.com/sh.json#'} will search for a shared schema added with $id: 'http://url.com/sh.json'

  • myField: { $ref: 'http://url.com/sh.json#/definitions/foo'} 将搜索使用 $id: 'http://url.com/sh.json' 添加的共享模式并使用字段 definitions.foo

    ¥myField: { $ref: 'http://url.com/sh.json#/definitions/foo'} will search for a shared schema added with $id: 'http://url.com/sh.json' and will use the field definitions.foo

  • myField: { $ref: 'http://url.com/sh.json#foo'} 将搜索使用 $id: 'http://url.com/sh.json' 添加的共享模式,并在其中查找具有 $id: '#foo' 的对象

    ¥myField: { $ref: 'http://url.com/sh.json#foo'} will search for a shared schema added with $id: 'http://url.com/sh.json' and it will look inside of it for object with $id: '#foo'

简单用法:

¥Simple usage:

fastify.addSchema({
$id: 'http://example.com/',
type: 'object',
properties: {
hello: { type: 'string' }
}
})

fastify.post('/', {
handler () {},
schema: {
body: {
type: 'array',
items: { $ref: 'http://example.com#/properties/hello' }
}
}
})

$ref 作为根引用:

¥$ref as root reference:

fastify.addSchema({
$id: 'commonSchema',
type: 'object',
properties: {
hello: { type: 'string' }
}
})

fastify.post('/', {
handler () {},
schema: {
body: { $ref: 'commonSchema#' },
headers: { $ref: 'commonSchema#' }
}
})

检索共享模式

¥Retrieving the shared schemas

如果验证器和序列化器是自定义的,则 .addSchema 方法将无用,因为参与者不再受 Fastify 控制。要访问添加到 Fastify 实例的模式,你只需使用 .getSchemas()

¥If the validator and the serializer are customized, the .addSchema method will not be useful since the actors are no longer controlled by Fastify. To access the schemas added to the Fastify instance, you can simply use .getSchemas():

fastify.addSchema({
$id: 'schemaId',
type: 'object',
properties: {
hello: { type: 'string' }
}
})

const mySchemas = fastify.getSchemas()
const mySchema = fastify.getSchema('schemaId')

与往常一样,函数 getSchemas 被封装并返回所选范围内可用的共享模式:

¥As usual, the function getSchemas is encapsulated and returns the shared schemas available in the selected scope:

fastify.addSchema({ $id: 'one', my: 'hello' })
// will return only `one` schema
fastify.get('/', (request, reply) => { reply.send(fastify.getSchemas()) })

fastify.register((instance, opts, done) => {
instance.addSchema({ $id: 'two', my: 'ciao' })
// will return `one` and `two` schemas
instance.get('/sub', (request, reply) => { reply.send(instance.getSchemas()) })

instance.register((subinstance, opts, done) => {
subinstance.addSchema({ $id: 'three', my: 'hola' })
// will return `one`, `two` and `three`
subinstance.get('/deep', (request, reply) => { reply.send(subinstance.getSchemas()) })
done()
})
done()
})

验证

¥Validation

路由验证内部依赖于 Ajv v8,这是一个高性能 JSON Schema 验证器。验证输入非常简单:只需在路由架构中添加所需的字段即可,就完成了!

¥The route validation internally relies upon Ajv v8 which is a high-performance JSON Schema validator. Validating the input is very easy: just add the fields that you need inside the route schema, and you are done!

支持的验证有:

¥The supported validations are:

  • body:验证请求的正文(如果是 POST、PUT 或 PATCH 方法)。

    ¥body: validates the body of the request if it is a POST, PUT, or PATCH method.

  • querystringquery:验证查询字符串。

    ¥querystring or query: validates the query string.

  • params:验证路由参数。

    ¥params: validates the route params.

  • headers:验证请求标头。

    ¥headers: validates the request headers.

所有验证都可以是一个完整的 JSON Schema 对象(具有 'object'type 属性和包含参数的 'properties' 对象)或更简单的变体,其中 typeproperties 属性被放弃并且参数列在顶层(参见下面的示例)。

¥All the validations can be a complete JSON Schema object (with a type property of 'object' and a 'properties' object containing parameters) or a simpler variation in which the type and properties attributes are forgone and the parameters are listed at the top level (see the example below).

ℹ 如果你需要使用最新版本的 Ajv (v8),你应该阅读 schemaController 部分中的操作方法。

¥ℹ If you need to use the latest version of Ajv (v8) you should read how to do it in the schemaController section.

示例:

¥Example:

const bodyJsonSchema = {
type: 'object',
required: ['requiredKey'],
properties: {
someKey: { type: 'string' },
someOtherKey: { type: 'number' },
requiredKey: {
type: 'array',
maxItems: 3,
items: { type: 'integer' }
},
nullableKey: { type: ['number', 'null'] }, // or { type: 'number', nullable: true }
multipleTypesKey: { type: ['boolean', 'number'] },
multipleRestrictedTypesKey: {
oneOf: [
{ type: 'string', maxLength: 5 },
{ type: 'number', minimum: 10 }
]
},
enumKey: {
type: 'string',
enum: ['John', 'Foo']
},
notTypeKey: {
not: { type: 'array' }
}
}
}

const queryStringJsonSchema = {
type: 'object',
properties: {
name: { type: 'string' },
excitement: { type: 'integer' }
}
}

const paramsJsonSchema = {
type: 'object',
properties: {
par1: { type: 'string' },
par2: { type: 'number' }
}
}

const headersJsonSchema = {
type: 'object',
properties: {
'x-foo': { type: 'string' }
},
required: ['x-foo']
}

const schema = {
body: bodyJsonSchema,
querystring: queryStringJsonSchema,
params: paramsJsonSchema,
headers: headersJsonSchema
}

fastify.post('/the/url', { schema }, handler)

对于 body 模式,可以通过将模式嵌套在 content 属性内来进一步区分每种内容类型的模式。将根据请求中的 Content-Type 标头应用架构验证。

¥For body schema, it is further possible to differentiate the schema per content type by nesting the schemas inside content property. The schema validation will be applied based on the Content-Type header in the request.

fastify.post('/the/url', {
schema: {
body: {
content: {
'application/json': {
schema: { type: 'object' }
},
'text/plain': {
schema: { type: 'string' }
}
// Other content types will not be validated
}
}
}
}, handler)

请注意,Ajv 将尝试将值 coerce 为架构 type 关键字中指定的类型,以通过验证并在之后使用正确类型的数据。

¥Note that Ajv will try to coerce the values to the types specified in your schema type keywords, both to pass the validation and to use the correctly typed data afterwards.

Fastify 中的 Ajv 默认配置支持在 querystring 中强制数组参数。示例:

¥The Ajv default configuration in Fastify supports coercing array parameters in querystring. Example:

const opts = {
schema: {
querystring: {
type: 'object',
properties: {
ids: {
type: 'array',
default: []
},
},
}
}
}

fastify.get('/', opts, (request, reply) => {
reply.send({ params: request.query }) // echo the querystring
})

fastify.listen({ port: 3000 }, (err) => {
if (err) throw err
})
curl -X GET "http://localhost:3000/?ids=1

{"params":{"ids":["1"]}}

你还可以为每个参数类型(正文、查询字符串、参数、标头)指定自定义架构验证器。

¥You can also specify a custom schema validator for each parameter type (body, querystring, params, headers).

例如,以下代码仅对 body 参数禁用类型强制,更改 ajv 默认选项:

¥For example, the following code disable type coercion only for the body parameters, changing the ajv default options:

const schemaCompilers = {
body: new Ajv({
removeAdditional: false,
coerceTypes: false,
allErrors: true
}),
params: new Ajv({
removeAdditional: false,
coerceTypes: true,
allErrors: true
}),
querystring: new Ajv({
removeAdditional: false,
coerceTypes: true,
allErrors: true
}),
headers: new Ajv({
removeAdditional: false,
coerceTypes: true,
allErrors: true
})
}

server.setValidatorCompiler(req => {
if (!req.httpPart) {
throw new Error('Missing httpPart')
}
const compiler = schemaCompilers[req.httpPart]
if (!compiler) {
throw new Error(`Missing compiler for ${req.httpPart}`)
}
return compiler.compile(req.schema)
})

有关更多信息,请参阅 此处

¥For further information see here

Ajv 插件

¥Ajv Plugins

你可以提供要与默认 ajv 实例一起使用的插件列表。请注意,插件必须与 Fastify 中附带的 Ajv 版本兼容。

¥You can provide a list of plugins you want to use with the default ajv instance. Note that the plugin must be compatible with the Ajv version shipped within Fastify.

请参阅 ajv options 检查插件格式

¥Refer to ajv options to check plugins format

const fastify = require('fastify')({
ajv: {
plugins: [
require('ajv-merge-patch')
]
}
})

fastify.post('/', {
handler (req, reply) { reply.send({ ok: 1 }) },
schema: {
body: {
$patch: {
source: {
type: 'object',
properties: {
q: {
type: 'string'
}
}
},
with: [
{
op: 'add',
path: '/properties/q',
value: { type: 'number' }
}
]
}
}
}
})

fastify.post('/foo', {
handler (req, reply) { reply.send({ ok: 1 }) },
schema: {
body: {
$merge: {
source: {
type: 'object',
properties: {
q: {
type: 'string'
}
}
},
with: {
required: ['q']
}
}
}
}
})

验证器编译器

¥Validator Compiler

validatorCompiler 是一个返回验证主体、URL 参数、标头和查询字符串的函数的函数。默认的 validatorCompiler 返回一个实现 ajv 验证接口的函数。Fastify 在内部使用它来加速验证。

¥The validatorCompiler is a function that returns a function that validates the body, URL parameters, headers, and query string. The default validatorCompiler returns a function that implements the ajv validation interface. Fastify uses it internally to speed the validation up.

Fastify 的 基线 ajv 配置 是:

¥Fastify's baseline ajv configuration is:

{
coerceTypes: 'array', // change data type of data to match type keyword
useDefaults: true, // replace missing properties and items with the values from corresponding default keyword
removeAdditional: true, // remove additional properties if additionalProperties is set to false, see: https://ajv.nodejs.cn/guide/modifying-data.html#removing-additional-properties
uriResolver: require('fast-uri'),
addUsedSchema: false,
// Explicitly set allErrors to `false`.
// When set to `true`, a DoS attack is possible.
allErrors: false
}

可以通过向你的 Fastify 工厂提供 ajv.customOptions 来修改此基线配置。

¥This baseline configuration can be modified by providing ajv.customOptions to your Fastify factory.

如果你想更改或设置其他配置选项,则需要创建自己的实例并覆盖现有实例,例如:

¥If you want to change or set additional config options, you will need to create your own instance and override the existing one like:

const fastify = require('fastify')()
const Ajv = require('ajv')
const ajv = new Ajv({
removeAdditional: 'all',
useDefaults: true,
coerceTypes: 'array',
// any other options
// ...
})
fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
return ajv.compile(schema)
})

注意:如果你使用任何验证器的自定义实例(甚至是 Ajv),则必须向验证器而不是 Fastify 添加模式,因为 Fastify 的默认验证器不再使用,并且 Fastify 的 addSchema 方法不知道你正在使用什么验证器。

¥*Note: If you use a custom instance of any validator (even Ajv), you have to add schemas to the validator instead of Fastify, since Fastify's default validator is no longer used, and Fastify's addSchema method has no idea what validator you are using.*

使用其他验证库

¥Using other validation libraries

setValidatorCompiler 函数可以轻松地用几乎任何 JavaScript 验证库(joiyup 等)或自定义库替换 ajv

¥The setValidatorCompiler function makes it easy to substitute ajv with almost any JavaScript validation library (joi, yup, ...) or a custom one:

const Joi = require('joi')

fastify.post('/the/url', {
schema: {
body: Joi.object().keys({
hello: Joi.string().required()
}).required()
},
validatorCompiler: ({ schema, method, url, httpPart }) => {
return data => schema.validate(data)
}
}, handler)
const yup = require('yup')
// Validation options to match ajv's baseline options used in Fastify
const yupOptions = {
strict: false,
abortEarly: false, // return all errors
stripUnknown: true, // remove additional properties
recursive: true
}

fastify.post('/the/url', {
schema: {
body: yup.object({
age: yup.number().integer().required(),
sub: yup.object().shape({
name: yup.string().required()
}).required()
})
},
validatorCompiler: ({ schema, method, url, httpPart }) => {
return function (data) {
// with option strict = false, yup `validateSync` function returns the
// coerced value if validation was successful, or throws if validation failed
try {
const result = schema.validateSync(data, yupOptions)
return { value: result }
} catch (e) {
return { error: e }
}
}
}
}, handler)
.statusCode 属性

¥.statusCode property

所有验证错误都将添加到设置为 400.statusCode 属性中。这保证了默认错误处理程序将响应的状态代码设置为 400

¥All validation errors will be added a .statusCode property set to 400. This guarantees that the default error handler will set the status code of the response to 400.

fastify.setErrorHandler(function (error, request, reply) {
request.log.error(error, `This error has status code ${error.statusCode}`)
reply.status(error.statusCode).send(error)
})
使用其他验证库的验证消息

¥Validation messages with other validation libraries

Fastify 的验证错误消息与默认验证引擎紧密耦合:从 ajv 返回的错误最终通过 schemaErrorFormatter 函数运行,该函数负责构建人性化的错误消息。但是,schemaErrorFormatter 函数是在考虑 ajv 的情况下编写的。因此,在使用其他验证库时,你可能会遇到奇怪或不完整的错误消息。

¥Fastify's validation error messages are tightly coupled to the default validation engine: errors returned from ajv are eventually run through the schemaErrorFormatter function which is responsible for building human-friendly error messages. However, the schemaErrorFormatter function is written with ajv in mind. As a result, you may run into odd or incomplete error messages when using other validation libraries.

为了规避这个问题,你有两个主要选择:

¥To circumvent this issue, you have 2 main options :

  1. 确保你的验证函数(由你的自定义 schemaCompiler 返回)以与 ajv 相同的结构和格式返回错误(尽管由于验证引擎之间的差异,这可能会变得困难和棘手)

    ¥make sure your validation function (returned by your custom schemaCompiler) returns errors in the same structure and format as ajv (although this could prove to be difficult and tricky due to differences between validation engines)

  2. 或者使用自定义 errorHandler 来拦截和格式化你的 'custom' 验证错误

    ¥or use a custom errorHandler to intercept and format your 'custom' validation errors

为帮助你编写自定义 errorHandler,Fastify 为所有验证错误添加了 2 个属性:

¥To help you in writing a custom errorHandler, Fastify adds 2 properties to all validation errors:

  • validation:验证函数返回的对象的 error 属性的内容(由你的自定义 schemaCompiler 返回)

    ¥validation: the content of the error property of the object returned by the validation function (returned by your custom schemaCompiler)

  • validationContext:发生验证错误的 'context'(正文、参数、查询、标头)

    ¥validationContext: the 'context' (body, params, query, headers) where the validation error occurred

下面显示了这种自定义 errorHandler 处理验证错误的一个非常做作的例子:

¥A very contrived example of such a custom errorHandler handling validation errors is shown below:

const errorHandler = (error, request, reply) => {
const statusCode = error.statusCode
let response

const { validation, validationContext } = error

// check if we have a validation error
if (validation) {
response = {
// validationContext will be 'body' or 'params' or 'headers' or 'query'
message: `A validation error occurred when validating the ${validationContext}...`,
// this is the result of your validation library...
errors: validation
}
} else {
response = {
message: 'An error occurred...'
}
}

// any additional work here, eg. log error
// ...

reply.status(statusCode).send(response)
}

序列化

¥Serialization

通常,你会将数据以 JSON 格式发送到客户端,Fastify 有一个强大的工具可以帮助你,即 fast-json-stringify,如果你在路由选项中提供了输出架构,则会使用它。我们鼓励你使用输出模式,因为它可以大大提高吞吐量并有助于防止敏感信息的意外泄露。

¥Usually, you will send your data to the clients as JSON, and Fastify has a powerful tool to help you, fast-json-stringify, which is used if you have provided an output schema in the route options. We encourage you to use an output schema, as it can drastically increase throughput and help prevent accidental disclosure of sensitive information.

示例:

¥Example:

const schema = {
response: {
200: {
type: 'object',
properties: {
value: { type: 'string' },
otherValue: { type: 'boolean' }
}
}
}
}

fastify.post('/the/url', { schema }, handler)

如你所见,响应架构基于状态代码。如果你想要对多个状态代码使用相同的模式,则可以使用 '2xx'default,例如:

¥As you can see, the response schema is based on the status code. If you want to use the same schema for multiple status codes, you can use '2xx' or default, for example:

const schema = {
response: {
default: {
type: 'object',
properties: {
error: {
type: 'boolean',
default: true
}
}
},
'2xx': {
type: 'object',
properties: {
value: { type: 'string' },
otherValue: { type: 'boolean' }
}
},
201: {
// the contract syntax
value: { type: 'string' }
}
}
}

fastify.post('/the/url', { schema }, handler)

你甚至可以为不同的内容类型制定特定的响应架构。例如:

¥You can even have a specific response schema for different content types. For example:

const schema = {
response: {
200: {
description: 'Response schema that support different content types'
content: {
'application/json': {
schema: {
name: { type: 'string' },
image: { type: 'string' },
address: { type: 'string' }
}
},
'application/vnd.v1+json': {
schema: {
type: 'array',
items: { $ref: 'test' }
}
}
}
},
'3xx': {
content: {
'application/vnd.v2+json': {
schema: {
fullName: { type: 'string' },
phone: { type: 'string' }
}
}
}
},
default: {
content: {
// */* is match-all content-type
'*/*': {
schema: {
desc: { type: 'string' }
}
}
}
}
}
}

fastify.post('/url', { schema }, handler)

串行器编译器

¥Serializer Compiler

serializerCompiler 是一个返回必须从输入对象返回字符串的函数的函数。当你定义响应 JSON 架构时,你可以通过提供一个函数来序列化你所做的每个路由,从而更改默认序列化方法。

¥The serializerCompiler is a function that returns a function that must return a string from an input object. When you define a response JSON Schema, you can change the default serialization method by providing a function to serialize every route where you do.

fastify.setSerializerCompiler(({ schema, method, url, httpStatus, contentType }) => {
return data => JSON.stringify(data)
})

fastify.get('/user', {
handler (req, reply) {
reply.send({ id: 1, name: 'Foo', image: 'BIG IMAGE' })
},
schema: {
response: {
'2xx': {
type: 'object',
properties: {
id: { type: 'number' },
name: { type: 'string' }
}
}
}
}
})

如果你在代码的特定部分需要自定义序列化器,则可以使用 reply.serializer(...) 设置一个。

¥If you need a custom serializer in a very specific part of your code, you can set one with reply.serializer(...).

错误处理

¥Error Handling

当请求的架构验证失败时,Fastify 将自动返回状态 400 响应,其中包括负载中验证器的结果。例如,如果你的路由具有以下架构

¥When schema validation fails for a request, Fastify will automatically return a status 400 response including the result from the validator in the payload. As an example, if you have the following schema for your route

const schema = {
body: {
type: 'object',
properties: {
name: { type: 'string' }
},
required: ['name']
}
}

如果不满足,路由将立即返回一个响应,其负载如下

¥and fail to satisfy it, the route will immediately return a response with the following payload

{
"statusCode": 400,
"error": "Bad Request",
"message": "body should have required property 'name'"
}

如果你想处理路由内部的错误,你可以为你的路由指定 attachValidation 选项。如果存在验证错误,请求的 validationError 属性将包含 Error 对象和原始 validation 结果,如下所示

¥If you want to handle errors inside the route, you can specify the attachValidation option for your route. If there is a validation error, the validationError property of the request will contain the Error object with the raw validation result as shown below

const fastify = Fastify()

fastify.post('/', { schema, attachValidation: true }, function (req, reply) {
if (req.validationError) {
// `req.validationError.validation` contains the raw validation error
reply.code(400).send(req.validationError)
}
})

schemaErrorFormatter

如果你想自己格式化错误,你可以提供一个同步函数,该函数必须在实例化时将错误作为 schemaErrorFormatter 选项返回给 Fastify。上下文函数将是 Fastify 服务器实例。

¥If you want to format errors yourself, you can provide a sync function that must return an error as the schemaErrorFormatter option to Fastify when instantiating. The context function will be the Fastify server instance.

errors 是 Fastify 模式错误 FastifySchemaValidationError 的数组。dataVar 是模式的当前验证部分。(参数 | 主体 | 查询字符串 | 标头)。

¥errors is an array of Fastify schema errors FastifySchemaValidationError. dataVar is the currently validated part of the schema. (params | body | querystring | headers).

const fastify = Fastify({
schemaErrorFormatter: (errors, dataVar) => {
// ... my formatting logic
return new Error(myErrorMessage)
}
})

// or
fastify.setSchemaErrorFormatter(function (errors, dataVar) {
this.log.error({ err: errors }, 'Validation failed')
// ... my formatting logic
return new Error(myErrorMessage)
})

你还可以使用 setErrorHandler 定义验证错误的自定义响应,例如

¥You can also use setErrorHandler to define a custom response for validation errors such as

fastify.setErrorHandler(function (error, request, reply) {
if (error.validation) {
reply.status(422).send(new Error('validation failed'))
}
})

如果你想要在架构中快速轻松地自定义错误响应,请查看 ajv-errors。查看 example 用法。

¥If you want a custom error response in the schema without headaches, and quickly, take a look at ajv-errors. Check out the example usage.

确保安装 ajv-errors 的 1.0.1 版本,因为它的后续版本与 AJV v6(Fastify v3 附带的版本)不兼容。

¥Make sure to install version 1.0.1 of ajv-errors, because later versions of it are not compatible with AJV v6 (the version shipped by Fastify v3).

以下是一个例子,展示了如何通过提供自定义 AJV 选项为模式的每个属性添加自定义错误消息。下面架构中的内联注释描述了如何配置它以针对每种情况显示不同的错误消息:

¥Below is an example showing how to add custom error messages for each property of a schema by supplying custom AJV options. Inline comments in the schema below describe how to configure it to show a different error message for each case:

const fastify = Fastify({
ajv: {
customOptions: {
jsonPointers: true,
// Warning: Enabling this option may lead to this security issue https://www.cvedetails.com/cve/CVE-2020-8192/
allErrors: true
},
plugins: [
require('ajv-errors')
]
}
})

const schema = {
body: {
type: 'object',
properties: {
name: {
type: 'string',
errorMessage: {
type: 'Bad name'
}
},
age: {
type: 'number',
errorMessage: {
type: 'Bad age', // specify custom message for
min: 'Too young' // all constraints except required
}
}
},
required: ['name', 'age'],
errorMessage: {
required: {
name: 'Why no name!', // specify error message for when the
age: 'Why no age!' // property is missing from input
}
}
}
}

fastify.post('/', { schema, }, (request, reply) => {
reply.send({
hello: 'world'
})
})

如果你想要返回本地化错误消息,请查看 ajv-i18n

¥If you want to return localized error messages, take a look at ajv-i18n

const localize = require('ajv-i18n')

const fastify = Fastify()

const schema = {
body: {
type: 'object',
properties: {
name: {
type: 'string',
},
age: {
type: 'number',
}
},
required: ['name', 'age'],
}
}

fastify.setErrorHandler(function (error, request, reply) {
if (error.validation) {
localize.ru(error.validation)
reply.status(400).send(error.validation)
return
}
reply.send(error)
})

JSON 模式支持

¥JSON Schema support

JSON Schema 提供了用于优化 schema 的实用程序,与 Fastify 的共享 schema 结合使用,可以让你轻松地重用所有 schema。

¥JSON Schema provides utilities to optimize your schemas that, in conjunction with Fastify's shared schema, let you reuse all your schemas easily.

使用案例验证器串行器
$ref$id️️ ✔️✔️
$ref/definitions✔️✔️
$ref 到共享模式 $id✔️✔️
$ref 到共享模式 /definitions✔️✔️

示例

¥Examples

在同一 JSON 架构中使用 $ref$id

¥Usage of $ref to $id in same JSON Schema

const refToId = {
type: 'object',
definitions: {
foo: {
$id: '#address',
type: 'object',
properties: {
city: { type: 'string' }
}
}
},
properties: {
home: { $ref: '#address' },
work: { $ref: '#address' }
}
}
在同一 JSON 架构中使用 $ref/definitions

¥Usage of $ref to /definitions in same JSON Schema

const refToDefinitions = {
type: 'object',
definitions: {
foo: {
$id: '#address',
type: 'object',
properties: {
city: { type: 'string' }
}
}
},
properties: {
home: { $ref: '#/definitions/foo' },
work: { $ref: '#/definitions/foo' }
}
}
使用 $ref 到共享架构 $id 作为外部架构

¥Usage $ref to a shared schema $id as external schema

fastify.addSchema({
$id: 'http://foo/common.json',
type: 'object',
definitions: {
foo: {
$id: '#address',
type: 'object',
properties: {
city: { type: 'string' }
}
}
}
})

const refToSharedSchemaId = {
type: 'object',
properties: {
home: { $ref: 'http://foo/common.json#address' },
work: { $ref: 'http://foo/common.json#address' }
}
}
使用 $ref 到共享架构 /definitions 作为外部架构

¥Usage $ref to a shared schema /definitions as external schema

fastify.addSchema({
$id: 'http://foo/shared.json',
type: 'object',
definitions: {
foo: {
type: 'object',
properties: {
city: { type: 'string' }
}
}
}
})

const refToSharedSchemaDefinitions = {
type: 'object',
properties: {
home: { $ref: 'http://foo/shared.json#/definitions/foo' },
work: { $ref: 'http://foo/shared.json#/definitions/foo' }
}
}

资源

¥Resources