路由
路由
¥Routes
路由方法配置应用的端点。可以使用简写方法或完整声明来声明路由。
¥The route methods configure the endpoints of the application. Routes can be declared using the shorthand method or the full declaration.
完整声明
¥Full declaration
fastify.route(options)
路由选项
¥Routes options
-
method
:目前支持GET
、HEAD
、TRACE
、DELETE
、OPTIONS
、PATCH
、PUT
和POST
。要接受更多方法,必须使用addHttpMethod
。它也可以是一系列方法。¥
method
: currently it supportsGET
,HEAD
,TRACE
,DELETE
,OPTIONS
,PATCH
,PUT
andPOST
. To accept more methods, theaddHttpMethod
must be used. It could also be an array of methods. -
url
:与此路由匹配的 URL 路径(别名:path
)。¥
url
: the path of the URL to match this route (alias:path
). -
schema
:包含请求和响应模式的对象。它们需要采用 JSON 结构 格式,请查看 此处 了解更多信息。¥
schema
: an object containing the schemas for the request and response. They need to be in JSON Schema format, check here for more info.-
body
:验证请求主体是否为 POST、PUT、PATCH、TRACE、SEARCH、PROPFIND、PROPPATCH 或 LOCK 方法。¥
body
: validates the body of the request if it is a POST, PUT, PATCH, TRACE, SEARCH, PROPFIND, PROPPATCH or LOCK method. -
querystring
或query
:验证查询字符串。这可以是一个完整的 JSON Schema 对象,具有object
的属性type
和参数的properties
对象,或者仅仅是properties
对象中包含的值,如下所示。¥
querystring
orquery
: validates the querystring. This can be a complete JSON Schema object, with the propertytype
ofobject
andproperties
object of parameters, or simply the values of what would be contained in theproperties
object as shown below. -
params
:验证参数。¥
params
: validates the params. -
response
:过滤并生成响应的模式,设置模式可以使我们的吞吐量提高 10-20%。¥
response
: filter and generate a schema for the response, setting a schema allows us to have 10-20% more throughput.
-
-
exposeHeadRoute
:为任何GET
路由创建同级HEAD
路由。默认为exposeHeadRoutes
实例选项的值。如果你想要自定义HEAD
处理程序而不禁用此选项,请确保在GET
路由之前定义它。¥
exposeHeadRoute
: creates a siblingHEAD
route for anyGET
routes. Defaults to the value ofexposeHeadRoutes
instance option. If you want a customHEAD
handler without disabling this option, make sure to define it before theGET
route. -
attachValidation
:如果存在模式验证错误,则将validationError
附加到请求,而不是将错误发送到错误处理程序。默认的 错误格式 是 Ajv。¥
attachValidation
: attachvalidationError
to request, if there is a schema validation error, instead of sending the error to the error handler. The default error format is the Ajv one. -
onRequest(request, reply, done)
:在收到请求后立即调用的 function,它也可以是一个函数数组。¥
onRequest(request, reply, done)
: a function called as soon as a request is received, it could also be an array of functions. -
preParsing(request, reply, done)
:在解析请求之前调用的 function,它也可以是一个函数数组。¥
preParsing(request, reply, done)
: a function called before parsing the request, it could also be an array of functions. -
preValidation(request, reply, done)
:在共享preValidation
钩子之后调用的 function,如果你需要在路由级别执行身份验证,则很有用,它也可以是一个函数数组。¥
preValidation(request, reply, done)
: a function called after the sharedpreValidation
hooks, useful if you need to perform authentication at route level for example, it could also be an array of functions. -
preHandler(request, reply, done)
:在请求处理程序之前调用的 function,它也可以是一个函数数组。¥
preHandler(request, reply, done)
: a function called just before the request handler, it could also be an array of functions. -
preSerialization(request, reply, payload, done)
:在序列化之前调用的 function,它也可以是一个函数数组。¥
preSerialization(request, reply, payload, done)
: a function called just before the serialization, it could also be an array of functions. -
onSend(request, reply, payload, done)
:在发送响应之前调用的 function,它也可以是一个函数数组。¥
onSend(request, reply, payload, done)
: a function called right before a response is sent, it could also be an array of functions. -
onResponse(request, reply, done)
:在发送响应后调用的 function,因此你将无法向客户端发送更多数据。它也可以是函数数组。¥
onResponse(request, reply, done)
: a function called when a response has been sent, so you will not be able to send more data to the client. It could also be an array of functions. -
onTimeout(request, reply, done)
:在请求超时且 HTTP 套接字已挂断时调用的 function。¥
onTimeout(request, reply, done)
: a function called when a request is timed out and the HTTP socket has been hung up. -
onError(request, reply, error, done)
:当路由处理程序抛出错误或将错误发送给客户端时,调用 function。¥
onError(request, reply, error, done)
: a function called when an Error is thrown or sent to the client by the route handler. -
handler(request, reply)
:将处理此请求的函数。当调用处理程序时,Fastify 服务器 将绑定到this
。注意:使用箭头函数将破坏this
的绑定。¥
handler(request, reply)
: the function that will handle this request. The Fastify server will be bound tothis
when the handler is called. Note: using an arrow function will break the binding ofthis
. -
errorHandler(error, request, reply)
:请求范围的自定义错误处理程序。覆盖默认错误全局处理程序以及setErrorHandler
设置的任何内容,用于对路由的请求。要访问默认处理程序,你可以访问instance.errorHandler
。请注意,只有当插件尚未覆盖它时,它才会指向 fastify 的默认errorHandler
。¥
errorHandler(error, request, reply)
: a custom error handler for the scope of the request. Overrides the default error global handler, and anything set bysetErrorHandler
, for requests to the route. To access the default handler, you can accessinstance.errorHandler
. Note that this will point to fastify's defaulterrorHandler
only if a plugin hasn't overridden it already. -
childLoggerFactory(logger, binding, opts, rawReq)
:一个自定义工厂函数,将被调用来为每个请求生成一个子日志器实例。请参阅childLoggerFactory
了解更多信息。覆盖默认日志器工厂以及setChildLoggerFactory
设置的任何内容,用于对路由的请求。要访问默认工厂,你可以访问instance.childLoggerFactory
。请注意,只有当插件尚未覆盖它时,它才会指向 Fastify 的默认childLoggerFactory
。¥
childLoggerFactory(logger, binding, opts, rawReq)
: a custom factory function that will be called to produce a child logger instance for every request. SeechildLoggerFactory
for more info. Overrides the default logger factory, and anything set bysetChildLoggerFactory
, for requests to the route. To access the default factory, you can accessinstance.childLoggerFactory
. Note that this will point to Fastify's defaultchildLoggerFactory
only if a plugin hasn't overridden it already. -
validatorCompiler({ schema, method, url, httpPart })
:构建请求验证模式的函数。参见 验证和序列化 文档。¥
validatorCompiler({ schema, method, url, httpPart })
: function that builds schemas for request validations. See the Validation and Serialization documentation. -
serializerCompiler({ { schema, method, url, httpStatus, contentType } })
:构建响应序列化模式的函数。参见 验证和序列化 文档。¥
serializerCompiler({ { schema, method, url, httpStatus, contentType } })
: function that builds schemas for response serialization. See the Validation and Serialization documentation. -
schemaErrorFormatter(errors, dataVar)
:格式化来自验证编译器的错误的函数。参见 验证和序列化 文档。覆盖全局架构错误格式化程序处理程序以及setSchemaErrorFormatter
设置的任何内容,用于对路由的请求。¥
schemaErrorFormatter(errors, dataVar)
: function that formats the errors from the validation compiler. See the Validation and Serialization documentation. Overrides the global schema error formatter handler, and anything set bysetSchemaErrorFormatter
, for requests to the route. -
bodyLimit
:阻止默认 JSON 正文解析器解析大于此字节数的请求正文。必须是整数。你也可以在首次使用fastify(options)
创建 Fastify 实例时全局设置此选项。默认为1048576
(1 MiB)。¥
bodyLimit
: prevents the default JSON body parser from parsing request bodies larger than this number of bytes. Must be an integer. You may also set this option globally when first creating the Fastify instance withfastify(options)
. Defaults to1048576
(1 MiB). -
logLevel
:设置此路由的日志级别。见下文。¥
logLevel
: set log level for this route. See below. -
logSerializers
:设置序列化器来记录此路由。¥
logSerializers
: set serializers to log for this route. -
config
:用于存储自定义配置的对象。¥
config
: object used to store custom configuration. -
version
:定义端点版本的 semver 兼容字符串。示例。¥
version
: a semver compatible string that defined the version of the endpoint. Example. -
constraints
:根据请求属性或值定义路由限制,使用 find-my-way 约束实现自定义匹配。包括内置的version
和host
约束,并支持自定义约束策略。¥
constraints
: defines route restrictions based on request properties or values, enabling customized matching using find-my-way constraints. Includes built-inversion
andhost
constraints, with support for custom constraint strategies. -
prefixTrailingSlash
:用于确定如何处理将/
作为带有前缀的路由传递的字符串。¥
prefixTrailingSlash
: string used to determine how to handle passing/
as a route with a prefix.-
both
(默认):将注册/prefix
和/prefix/
。¥
both
(default): Will register both/prefix
and/prefix/
. -
slash
:将仅注册/prefix/
。¥
slash
: Will register only/prefix/
. -
no-slash
:将仅注册/prefix
。¥
no-slash
: Will register only/prefix
.
注意:此选项不会覆盖 Server 配置中的
ignoreTrailingSlash
。¥Note: this option does not override
ignoreTrailingSlash
in Server configuration. -
-
request
在 请求 中定义。¥
request
is defined in Request. -
reply
在 响应 中定义。¥
reply
is defined in Reply.
🛈 注意:
onRequest
、preParsing
、preValidation
、preHandler
、preSerialization
、onSend
和onResponse
的文档在 钩子 中有详细说明。要在handler
处理请求之前发送响应,请参阅 响应来自钩子的请求。¥🛈 Note: The documentation for
onRequest
,preParsing
,preValidation
,preHandler
,preSerialization
,onSend
, andonResponse
is detailed in Hooks. To send a response before the request is handled by thehandler
, see Respond to a request from a hook.
示例:
¥Example:
fastify.route({
method: 'GET',
url: '/',
schema: {
querystring: {
type: 'object',
properties: {
name: { type: 'string' },
excitement: { type: 'integer' }
}
},
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
速记声明
¥Shorthand declaration
上面的路由声明更像 Hapi,但如果你更喜欢 Express/Restify 方法,我们也支持它:
¥The above route declaration is more Hapi-like, but if you prefer an Express/Restify approach, we support it as well:
fastify.get(path, [options], handler)
fastify.head(path, [options], handler)
fastify.post(path, [options], handler)
fastify.put(path, [options], handler)
fastify.delete(path, [options], handler)
fastify.options(path, [options], handler)
fastify.patch(path, [options], handler)
示例:
¥Example:
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}
fastify.get('/', opts, (request, reply) => {
reply.send({ hello: 'world' })
})
fastify.all(path, [options], handler)
将向所有支持的方法添加相同的处理程序。
¥fastify.all(path, [options], handler)
will add the same handler to all the
supported methods.
处理程序也可以通过 options
对象提供:
¥The handler may also be supplied via the options
object:
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
}
fastify.get('/', opts)
🛈 注意:在
options
和快捷方式方法的第三个参数中指定处理程序会引发重复的handler
错误。¥🛈 Note: Specifying the handler in both
options
and as the third parameter to the shortcut method throws a duplicatehandler
error.
网址建设
¥Url building
Fastify 支持静态和动态 URL。
¥Fastify supports both static and dynamic URLs.
要注册参数路径,请在参数名称前使用冒号。对于通配符,请使用星号。始终在参数和通配符路由之前检查静态路由。
¥To register a parametric path, use a colon before the parameter name. For wildcard, use a star. Static routes are always checked before parametric and wildcard routes.
// parametric
fastify.get('/example/:userId', function (request, reply) {
// curl ${app-url}/example/12345
// userId === '12345'
const { userId } = request.params;
// your code here
})
fastify.get('/example/:userId/:secretToken', function (request, reply) {
// curl ${app-url}/example/12345/abc.zHi
// userId === '12345'
// secretToken === 'abc.zHi'
const { userId, secretToken } = request.params;
// your code here
})
// wildcard
fastify.get('/example/*', function (request, reply) {})
支持正则表达式路由,但必须对斜杠进行转义。请注意,RegExp 在性能方面也非常昂贵!
¥Regular expression routes are supported, but slashes must be escaped. Take note that RegExp is also very expensive in terms of performance!
// parametric with regexp
fastify.get('/example/:file(^\\d+).png', function (request, reply) {
// curl ${app-url}/example/12345.png
// file === '12345'
const { file } = request.params;
// your code here
})
可以在同一对斜线("/")中定义多个参数。例如:
¥It is possible to define more than one parameter within the same couple of slash ("/"). Such as:
fastify.get('/example/near/:lat-:lng/radius/:r', function (request, reply) {
// curl ${app-url}/example/near/15°N-30°E/radius/20
// lat === "15°N"
// lng === "30°E"
// r ==="20"
const { lat, lng, r } = request.params;
// your code here
})
请记住在这种情况下使用破折号 ("*") 作为参数分隔符。
¥Remember in this case to use the dash ("-") as parameters separator.
最后,RegExp 可以有多个参数:
¥Finally, it is possible to have multiple parameters with RegExp:
fastify.get('/example/at/:hour(^\\d{2})h:minute(^\\d{2})m', function (request, reply) {
// curl ${app-url}/example/at/08h24m
// hour === "08"
// minute === "24"
const { hour, minute } = request.params;
// your code here
})
在这种情况下,可以使用正则表达式不匹配的任何字符作为参数分隔符。
¥In this case as parameter separator it is possible to use whatever character is not matched by the regular expression.
可以通过在参数名称末尾添加问号("?")使最后一个参数成为可选参数。
¥The last parameter can be made optional by adding a question mark ("?") to the end of the parameter name.
fastify.get('/example/posts/:id?', function (request, reply) {
const { id } = request.params;
// your code here
})
在这种情况下,/example/posts
和 /example/posts/1
都有效。如果未指定,可选参数将为 undefined
。
¥In this case, /example/posts
and /example/posts/1
are both valid. The
optional param will be undefined
if not specified.
具有多个参数的路由可能会对性能产生负面影响。最好使用单参数方法,尤其是在应用热路径上的路由上。有关更多详细信息,请参阅 find-my-way。
¥Having a route with multiple parameters may negatively affect performance. Prefer a single parameter approach, especially on routes that are on the hot path of your application. For more details, see find-my-way.
要在路径中包含冒号而不声明参数,请使用双冒号。例如:
¥To include a colon in a path without declaring a parameter, use a double colon. For example:
fastify.post('/name::verb') // will be interpreted as /name:verb
异步等待
¥Async Await
你是 async/await
用户吗?我们为你服务!
¥Are you an async/await
user? We have you covered!
fastify.get('/', options, async function (request, reply) {
const data = await getData()
const processed = await processData(data)
return processed
})
如图所示,不会调用 reply.send
将数据发送回用户。只需返回主体,你就大功告成了!
¥As shown, reply.send
is not called to send data back to the user. Simply
return the body and you are done!
如果需要,你还可以使用 reply.send
发回数据。在这种情况下,请不要忘记在 async
处理程序中使用 return reply
或 await reply
,以避免出现竞争条件。
¥If needed, you can also send data back with reply.send
. In this case, do not
forget to return reply
or await reply
in your async
handler to avoid race
conditions.
fastify.get('/', options, async function (request, reply) {
const data = await getData()
const processed = await processData(data)
return reply.send(processed)
})
如果路由封装了基于回调的 API,该 API 将在 promise 链之外调用 reply.send()
,则可以 await reply
:
¥If the route is wrapping a callback-based API that will call reply.send()
outside of the promise chain, it is possible to await reply
:
fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
await reply
})
返回响应也有效:
¥Returning reply also works:
fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
return reply
})
⚠ 警告:
¥⚠ Warning:
当同时使用
return value
和reply.send(value)
时,第一个优先,第二个被丢弃,并发出警告日志。¥When using both
return value
andreply.send(value)
, the first one takes precedence, the second is discarded, and a warn log is emitted.可以在 promise 之外调用
reply.send()
,但需要特别注意。参见 promise-resolution。¥Calling
reply.send()
outside of the promise is possible but requires special attention. See promise-resolution.无法返回
undefined
。参见 promise-resolution。¥
undefined
cannot be returned. See promise-resolution.
Promise 决议
¥Promise resolution
如果处理程序是 async
函数或返回 promise,请注意支持回调和 promise 控制流的特殊行为。当处理程序的 promise 解决时,除非你在处理程序中明确等待或返回 reply
,否则将自动发送回复及其值。
¥If the handler is an async
function or returns a promise, be aware of the
special behavior to support callback and promise control-flow. When the
handler's promise resolves, the reply is automatically sent with its value
unless you explicitly await or return reply
in the handler.
-
如果使用
async/await
或 promise 但使用reply.send
响应:¥If using
async/await
or promises but responding withreply.send
:-
执行
return reply
/await reply
。¥Do
return reply
/await reply
. -
不要忘记调用
reply.send
。¥Do not forget to call
reply.send
.
-
-
如果使用
async/await
或 promise:¥If using
async/await
or promises:-
不要使用
reply.send
。¥Do not use
reply.send
. -
请返回要发送的值。
¥Do return the value to send.
-
此方法以最小的权衡支持 callback-style
和 async-await
。但是,建议仅使用一种样式来在应用中进行一致的错误处理。
¥This approach supports both callback-style
and async-await
with minimal
trade-off. However, it is recommended to use only one style for consistent
error handling within your application.
🛈 注意:每个异步函数都会返回一个 promise。
¥🛈 Note: Every async function returns a promise by itself.
路由前缀
¥Route Prefixing
有时需要维护同一 API 的多个版本。一种常见的方法是在路由前加上 API 版本号,例如 /v1/user
。Fastify 提供了一种快速而智能的方法来创建同一 API 的不同版本,而无需手动更改所有路由名称,这称为路由前缀。工作原理如下:
¥Sometimes maintaining multiple versions of the same API is necessary. A common
approach is to prefix routes with the API version number, e.g., /v1/user
.
Fastify offers a fast and smart way to create different versions of the same API
without changing all the route names by hand, called route prefixing. Here is
how it works:
// server.js
const fastify = require('fastify')()
fastify.register(require('./routes/v1/users'), { prefix: '/v1' })
fastify.register(require('./routes/v2/users'), { prefix: '/v2' })
fastify.listen({ port: 3000 })
// routes/v1/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v1)
done()
}
// routes/v2/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v2)
done()
}
Fastify 不会抱怨对两个不同的路由使用相同的名称,因为它会在编译时自动处理前缀。这可确保性能不受影响。
¥Fastify will not complain about using the same name for two different routes because it handles the prefix automatically at compilation time. This ensures performance is not affected.
现在客户端可以访问以下路由:
¥Now clients will have access to the following routes:
-
/v1/user
-
/v2/user
这可以多次完成,并且适用于嵌套 register
。路由参数也受支持。
¥This can be done multiple times and works for nested register
. Route
parameters are also supported.
要对所有路由使用前缀,请将它们放在插件内:
¥To use a prefix for all routes, place them inside a plugin:
const fastify = require('fastify')()
const route = {
method: 'POST',
url: '/login',
handler: () => {},
schema: {},
}
fastify.register(function (app, _, done) {
app.get('/users', () => {})
app.route(route)
done()
}, { prefix: '/v1' }) // global route prefix
await fastify.listen({ port: 3000 })
路由前缀和 fastify-plugin
¥Route Prefixing and fastify-plugin
如果使用 fastify-plugin
封装路由,则此选项不起作用。要使其工作,请将插件封装在插件中:
¥If using fastify-plugin
to wrap
routes, this option will not work. To make it work, wrap a plugin in a plugin:
const fp = require('fastify-plugin')
const routes = require('./lib/routes')
module.exports = fp(async function (app, opts) {
app.register(routes, {
prefix: '/v1',
})
}, {
name: 'my-routes'
})
处理前缀插件内的 / 路由
¥Handling of / route inside prefixed plugins
/
路由的行为根据前缀是否以 /
结尾而不同。例如,使用前缀 /something/
,添加 /
路由仅匹配 /something/
。使用前缀 /something
,添加 /
路由可匹配 /something
和 /something/
。
¥The /
route behaves differently based on whether the prefix ends with /
.
For example, with a prefix /something/
, adding a /
route matches only
/something/
. With a prefix /something
, adding a /
route matches both
/something
and /something/
.
有关更多信息,请参阅上面的 prefixTrailingSlash
路由选项以更改此行为。
¥See the prefixTrailingSlash
route option above to change this behavior.
自定义日志级别
¥Custom Log Level
可以通过将 logLevel
选项传递给插件或具有所需 value 的路由,为 Fastify 中的路由设置不同的日志级别。
¥Different log levels can be set for routes in Fastify by passing the logLevel
option to the plugin or route with the desired
value.
请注意,在插件级别设置 logLevel
也会影响 setNotFoundHandler
和 setErrorHandler
。
¥Be aware that setting logLevel
at the plugin level also affects
setNotFoundHandler
and
setErrorHandler
.
// server.js
const fastify = require('fastify')({ logger: true })
fastify.register(require('./routes/user'), { logLevel: 'warn' })
fastify.register(require('./routes/events'), { logLevel: 'debug' })
fastify.listen({ port: 3000 })
或者直接将其传递给路由:
¥Or pass it directly to a route:
fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
reply.send({ hello: 'world' })
})
请记住,自定义日志级别仅适用于路由,而不适用于可通过 fastify.log
访问的全局 Fastify Logger。
¥Remember that the custom log level applies only to routes, not to the global
Fastify Logger, accessible with fastify.log
.
自定义日志序列化器
¥Custom Log Serializer
在某些情况下,记录大型对象可能会浪费资源。定义自定义 serializers
并将其附加到适当的上下文中。
¥In some contexts, logging a large object may waste resources. Define custom
serializers
and attach them in the appropriate context.
const fastify = require('fastify')({ logger: true })
fastify.register(require('./routes/user'), {
logSerializers: {
user: (value) => `My serializer one - ${value.name}`
}
})
fastify.register(require('./routes/events'), {
logSerializers: {
user: (value) => `My serializer two - ${value.name} ${value.surname}`
}
})
fastify.listen({ port: 3000 })
序列化器可以通过上下文继承:
¥Serializers can be inherited by context:
const fastify = Fastify({
logger: {
level: 'info',
serializers: {
user (req) {
return {
method: req.method,
url: req.url,
headers: req.headers,
host: req.host,
remoteAddress: req.ip,
remotePort: req.socket.remotePort
}
}
}
}
})
fastify.register(context1, {
logSerializers: {
user: value => `My serializer father - ${value}`
}
})
async function context1 (fastify, opts) {
fastify.get('/', (req, reply) => {
req.log.info({ user: 'call father serializer', key: 'another key' })
// shows: { user: 'My serializer father - call father serializer', key: 'another key' }
reply.send({})
})
}
fastify.listen({ port: 3000 })
配置
¥Config
注册一个新的处理程序,你可以将配置对象传递给它并在处理程序中检索它。
¥Registering a new handler, you can pass a configuration object to it and retrieve it in the handler.
// server.js
const fastify = require('fastify')()
function handler (req, reply) {
reply.send(reply.routeOptions.config.output)
}
fastify.get('/en', { config: { output: 'hello world!' } }, handler)
fastify.get('/it', { config: { output: 'ciao mondo!' } }, handler)
fastify.listen({ port: 3000 })
约束条件
¥Constraints
Fastify 支持通过 find-my-way
约束根据 Host
标头或任何其他值等属性约束路由以匹配某些请求。约束在路由选项的 constraints
属性中指定。Fastify 有两个内置约束:version
和 host
。可以添加自定义约束策略来检查请求的其他部分,以决定是否应该执行路由。
¥Fastify supports constraining routes to match certain requests based on
properties like the Host
header or any other value via
find-my-way
constraints.
Constraints are specified in the constraints
property of the route options.
Fastify has two built-in constraints: version
and host
. Custom constraint
strategies can be added to inspect other parts of a request to decide if a route
should be executed.
版本限制
¥Version Constraints
你可以在 constraints
选项中为路由提供 version
键。版本化路由允许为同一 HTTP 路由路径声明多个处理程序,并根据请求的 Accept-Version
标头进行匹配。Accept-Version
标头值应遵循 semver 规范,并且应使用精确的 semver 版本声明路由以进行匹配。
¥You can provide a version
key in the constraints
option to a route.
Versioned routes allows multiple handlers to be declared for the same HTTP
route path, matched according to the request's Accept-Version
header.
The Accept-Version
header value should follow the
semver specification, and routes should be declared
with exact semver versions for matching.
如果路由设置了版本,Fastify 将要求设置请求 Accept-Version
标头,并且对于同一路径,将优先使用版本化路由而不是非版本化路由。目前不支持高级版本范围和预发行版。
¥Fastify will require a request Accept-Version
header to be set if the route
has a version set, and will prefer a versioned route to a non-versioned route
for the same path. Advanced version ranges and pre-releases currently are not
supported.
请注意,使用此功能会导致路由整体性能下降。
¥Be aware that using this feature will cause a degradation of the overall performances of the router.
fastify.route({
method: 'GET',
url: '/',
constraints: { version: '1.2.0' },
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'Accept-Version': '1.x' // it could also be '1.2.0' or '1.2.x'
}
}, (err, res) => {
// { hello: 'world' }
})
⚠ 警告:在响应中设置
Vary
标头,并使用用于版本控制的值(例如'Accept-Version'
)来防止缓存中毒攻击。这也可以在代理/CDN 中配置。¥⚠ Warning: Set a
Vary
header in responses with the value used for versioning (e.g.,'Accept-Version'
) to prevent cache poisoning attacks. This can also be configured in a Proxy/CDN.const append = require('vary').append
fastify.addHook('onSend', (req, reply, payload, done) => {
if (req.headers['accept-version']) { // or the custom header being used
let value = reply.getHeader('Vary') || ''
const header = Array.isArray(value) ? value.join(', ') : String(value)
if ((value = append(header, 'Accept-Version'))) { // or the custom header being used
reply.header('Vary', value)
}
}
done()
})
如果声明了具有相同主版本或次版本的多个版本,Fastify 将始终选择与 Accept-Version
标头值兼容最高的版本。
¥If multiple versions with the same major or minor are declared, Fastify will
always choose the highest compatible with the Accept-Version
header value.
如果请求缺少 Accept-Version
标头,则将返回 404 错误。
¥If the request lacks an Accept-Version
header, a 404 error will be returned.
创建 Fastify 服务器实例时,可以通过 constraints
配置定义自定义版本匹配逻辑。
¥Custom version matching logic can be defined through the
constraints
configuration when creating a Fastify
server instance.
主机限制
¥Host Constraints
在 constraints
路由选项中提供 host
键,以将路由限制为请求 Host
标头的某些值。host
约束值可以指定为精确匹配的字符串或任意主机匹配的 RegExp。
¥Provide a host
key in the constraints
route option to limit the route to
certain values of the request Host
header. host
constraint values can be
specified as strings for exact matches or RegExps for arbitrary host matching.
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'auth.fastify.dev' },
handler: function (request, reply) {
reply.send('hello world from auth.fastify.dev')
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'Host': 'example.com'
}
}, (err, res) => {
// 404 because the host doesn't match the constraint
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'Host': 'auth.fastify.dev'
}
}, (err, res) => {
// => 'hello world from auth.fastify.dev'
})
还可以指定 RegExp host
约束,允许限制主机匹配通配符子域(或任何其他模式):
¥RegExp host
constraints can also be specified allowing constraining to hosts
matching wildcard subdomains (or any other pattern):
fastify.route({
method: 'GET',
url: '/',
constraints: { host: /.*\.fastify\.dev/ }, // will match any subdomain of fastify.dev
handler: function (request, reply) {
reply.send('hello world from ' + request.headers.host)
}
})
异步自定义约束
¥Asynchronous Custom Constraints
可以提供自定义约束,并且可以从数据库等其他来源获取 constraint
标准。将异步自定义约束用作最后的手段,因为它们会影响路由性能。
¥Custom constraints can be provided, and the constraint
criteria can be
fetched from another source such as a database. Use asynchronous custom
constraints as a last resort, as they impact router performance.
function databaseOperation(field, done) {
done(null, field)
}
const secret = {
// strategy name for referencing in the route handler `constraints` options
name: 'secret',
// storage factory for storing routes in the find-my-way route tree
storage: function () {
let handlers = {}
return {
get: (type) => { return handlers[type] || null },
set: (type, store) => { handlers[type] = store }
}
},
// function to get the value of the constraint from each incoming request
deriveConstraint: (req, ctx, done) => {
databaseOperation(req.headers['secret'], done)
},
// optional flag marking if handlers without constraints can match requests that have a value for this constraint
mustMatchWhenDerived: true
}
⚠ 警告:当使用异步约束时,请避免在回调中返回错误。如果错误不可避免,请提供自定义
frameworkErrors
处理程序来管理它们。否则,路由选择可能会破坏或暴露敏感信息。¥⚠ Warning: When using asynchronous constraints, avoid returning errors inside the callback. If errors are unavoidable, provide a custom
frameworkErrors
handler to manage them. Otherwise, route selection may break or expose sensitive information.const Fastify = require('fastify')
const fastify = Fastify({
frameworkErrors: function (err, res, res) {
if (err instanceof Fastify.errorCodes.FST_ERR_ASYNC_CONSTRAINT) {
res.code(400)
return res.send("Invalid header provided")
} else {
res.send(err)
}
}
})