2025-03-29 14:35:49 +08:00

11 KiB
Raw Blame History

fastify

快速开始

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' }
})

还可以验证 bodyquerystringparams 以及 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