验证和序列化
验证和序列化
¥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()
动态评估代码,这对于与用户提供的架构一起使用是不安全的。请参阅 Ajv 和 fast-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 runasync
tasks, use Fastify's hooks instead after validation completes, such aspreHandler
.
核心理念
¥Core concepts
验证和序列化任务由两个不同的、可定制的参与者处理:
¥The validation and the serialization tasks are processed by two different, and customizable, actors:
Ajv v8 用于验证请求
¥Ajv v8 for the validation of a request
fast-json-stringify 用于序列化响应主体
¥fast-json-stringify for the serialization of a response's body
这两个独立的实体仅共享通过 .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 schemamyField: { $ref: '#/definitions/foo'}
将在当前模式中搜索字段definitions.foo
¥
myField: { $ref: '#/definitions/foo'}
will search for fielddefinitions.foo
inside the current schemamyField: { $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 fielddefinitions.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.querystring
或query
:验证查询字符串。¥
querystring
orquery
: validates the query string.params
:验证路由参数。¥
params
: validates the route params.headers
:验证请求标头。¥
headers
: validates the request headers.
所有验证都可以是一个完整的 JSON Schema 对象(具有 'object'
的 type
属性和包含参数的 'properties'
对象)或更简单的变体,其中 type
和 properties
属性被放弃并且参数列在顶层(参见下面的示例)。
¥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 验证库(joi、yup 等)或自定义库替换 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 :
确保你的验证函数(由你的自定义
schemaCompiler
返回)以与ajv
相同的结构和格式返回错误(尽管由于验证引擎之间的差异,这可能会变得困难和棘手)¥make sure your validation function (returned by your custom
schemaCompiler
) returns errors in the same structure and format asajv
(although this could prove to be difficult and tricky due to differences between validation engines)或者使用自定义
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 theerror
property of the object returned by the validation function (returned by your customschemaCompiler
)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