11 KiB
11 KiB
fastify
- 官网: https://fastify.dev
- 中文文档: https://www.fastify.cn/docs/latest/Getting-Started/
- 提升 Node.js 服务端性能:Fastify 框架
- Fastify+TS实现基础IM服务
快速开始
pnpm add fastify
// Import the framework and instantiate it
import Fastify from "fastify";
const fastify = Fastify({
logger: true,
});
// Declare a route
fastify.get("/", async function handler(request, reply) {
return { hello: "world" };
});
// Run the server!
try {
await fastify.listen({ port: 3000 });
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
因为使用esm模块, package.json
需要设置"type": "module"
使用cli快速生成项目
npm install --global fastify-cli
fastify generate myproject
插件、装饰器、中间件、钩子
插件
用于添加功能、共享代码或封装逻辑
===在 Fastify 中,一切都是插件===
- 一个插件可以是一个函数,该函数接受 Fastify 实例、选项和回调函数作为参数
- 插件可以注册路由、添加装饰器、声明新的钩子,甚至可以封装其他插件,用于构建模块化的应用
- 在应用启动时加载,按照注册的顺序执行
fastify.register()
例子:
async function routes (fastify, options) {
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
}
export default routes
import Fastify from 'fastify'
import routes from './routers/root.js'
const fastify = Fastify({
logger: true
})
// 使用register注册
fastify.register(routes)
try {
await fastify.listen({ port: 3000 })
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
装饰器
扩展 Fastify 实例、请求(Request)和回复(Reply)对象,通过添加新的方法或属性
- 例如,可以添加一个装饰器来添加一个方法,该方法在每个请求中都可用,用于访问共享的配置数据或服务
中间件
支持使用 Express/Connect 风格的中间件,主要用于兼容性或特定功能的集成
- 应谨慎使用,因为不当使用可能会绕过 Fastify 的一些优化,影响性能
钩子
允许开发者在请求生命周期的不同阶段介入执行逻辑
- 例如,在请求接收之后、路由解析之前、发送响应之前等
- 可以用于执行一些预处理或后处理逻辑,如权限检查、请求日志记录、修改响应等
fastify.addHook
常见的钩子:
onRequest
:在请求被接收后立即执行,但在任何其他处理之前。preParsing
:在请求体解析之前执行。preValidation
:在路由级别的验证之前执行。preHandler
:在路由处理函数之前执行。preSerialization
:在响应被序列化之前执行,发送给客户端之前。onSend
:在响应发送给客户端之前,但在序列化之后执行。onResponse
:在响应完全发送给客户端后执行。
钩子的使用
fastify.get(
'/example',
{
preHandler: (request, reply, done) => {
// 在路由处理器之前执行某些代码
done()
},
},
(request, reply) => {
reply.send({ hello: 'world' })
}
)
生命周期
Incoming Request
│
└─▶ Routing
│
└─▶ Instance Logger
│
4**/5** ◀─┴─▶ onRequest Hook 在请求完全被接收之后,任何解析之前触发.用来执行一些认证或者其他检查
│
4**/5** ◀─┴─▶ preParsing Hook 解析请求之前触发,但在它被路由处理之后。用来修改原始请求对象,例如解密数据。
│
4**/5** ◀─┴─▶ Parsing
│
4**/5** ◀─┴─▶ preValidation Hook 在执行路由级别的验证之前触发.用来在标准验证之前预处理一些参数
│
400 ◀─┴─▶ Validation
│
4**/5** ◀─┴─▶ preHandler Hook 在执行路由的处理函数之前触发。这是处理请求前最后的步骤,可以做一些预处理工作,如权限校验。
│
4**/5** ◀─┴─▶ User Handler
│
└─▶ Reply
│
4**/5** ◀─┴─▶ preSerialization Hook 在发送响应之前,序列化之前触发。可以用来修改响应数据,或者在序列化复杂类型之前进行转换。
│
└─▶ onSend Hook 可以修改响应体或者添加响应头
│
4**/5** ◀─┴─▶ Outgoing Response
│
└─▶ onResponse Hook 主要用于记录日志或者执行清理任务
响应的数据流
★ schema validation Error
│
└─▶ schemaErrorFormatter
│
reply sent ◀── JSON ─┴─ Error instance
│
│ ★ throw an Error
★ send or return │ │
│ │ │
│ ▼ │
reply sent ◀── JSON ─┴─ Error instance ──▶ setErrorHandler ◀─────┘
│
reply sent ◀── JSON ─┴─ Error instance ──▶ onError Hook
│
└─▶ reply sent
添加钩子
fastify.addHook('preHandler', (request, reply, done) => {
// some code
done()
})
JSON Schema (验证数据)
验证客户端请求的数据和格式化响应数据
- 验证路由参数、查询字符串、请求体和响应体
在 schema 的选项中设置 response
的值,能够加快 JSON 的序列化, 因为 Fastify 仅对 schema 里出现的数据进行序列化。
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}
fastify.get('/', opts, async (request, reply) => {
return { hello: 'world' }
})
还可以验证 body
、querystring
、params
以及 header
。
路由
实际项目中, 路由分散在不同的文件和模块中, 使用插件注册
自定义路由处理器
const customHandler = (request, reply) => {
// 自定义处理逻辑
};
fastify.route({
method: 'GET',
url: '/custom',
handler: customHandler
});
内置的快速方法
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)
// 你还可以链式调用它们
fastify.get('/path', handler).post('/path', handler);
静态路由
// 参数
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
})
// 通配符
fastify.get('/example/*', function (request, reply) {})
动态路由参数
fastify.get('/user/:id', (request, reply) => {
const userId = request.params.id;
// 根据 userId 获取用户信息...
});
路由前缀
fastify.register(require('./user-routes'), { prefix: '/api/v1/users' });
404处理
fastify.setNotFoundHandler((request, reply) => {
reply.status(404).send({ error: 'Not Found' });
});
路由级别的插件
为特定的路由添加特定的功能,比如认证、日志记录等
fastify.get('/private', { preHandler: fastify.auth }, (request, reply) => {
// 只有通过认证的请求才能到达这里
});
websocket
fastify.get('/ws', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
connection.socket.on('message', message => {
// 处理 WebSocket 消息
});
});
高级查询解析
自定义查询字符串的解析器
const qs = require('qs');
fastify.setQuerystringParser((str) => {
return qs.parse(str);
});
请求和响应对象
请求对象
request.raw
: 原生的 Node.js HTTP 请求对象。request.body
: 请求体的内容,Fastify 会根据你设置的内容类型自动解析它。request.query
: 解析后的查询字符串参数。request.params
: 路由参数值,例如/user/:id
中的id
。request.headers
: 请求头对象。request.id
: 每个请求的唯一标识符,可以用于日志记录或跟踪。request.log
: 日志记录器实例,可以用来记录请求相关的日志。request.ip
: 客户端的 IP 地址。request.method
: HTTP 请求方法(例如,GET
,POST
等)。request.url
: 请求的 URL 字符串。
响应对象
reply.raw
: 原生的 Node.js HTTP 响应对象。reply.code(statusCode)
: 设置响应的状态码。reply.header(name, value)
: 设置响应头。reply.headers(headers)
: 一次性设置多个响应头。reply.type(contentType)
: 设置Content-Type
响应头。reply.redirect(url)
: 重定向到指定的 URL。reply.send(payload)
: 发送响应数据。payload 可以是一个字符串、Buffer、对象或者 Stream。reply.serializer(customSerializer)
: 设置自定义的序列化函数来转换响应数据。reply.sent
: 一个布尔值,如果响应已经发送则为true
。