路由基础 #

一、路由概述 #

路由是Web框架的核心功能,Fastify提供了强大且灵活的路由系统。

1.1 什么是路由 #

路由决定了如何响应客户端对特定端点的请求,包含:

  • HTTP方法:GET、POST、PUT、DELETE等
  • URL路径:/users、/products/:id等
  • 处理函数:处理请求并返回响应

1.2 路由注册方式 #

Fastify提供两种路由注册方式:

javascript
// 简写方式
fastify.get('/path', handler)

// 完整方式
fastify.route({
  method: 'GET',
  url: '/path',
  handler: handler
})

二、基本路由 #

2.1 定义基本路由 #

javascript
const fastify = require('fastify')({ logger: true })

fastify.get('/', async (request, reply) => {
  return { message: 'Hello World' }
})

fastify.post('/users', async (request, reply) => {
  return { message: 'User created' }
})

fastify.put('/users/:id', async (request, reply) => {
  return { message: 'User updated', id: request.params.id }
})

fastify.delete('/users/:id', async (request, reply) => {
  return { message: 'User deleted', id: request.params.id }
})

fastify.listen({ port: 3000 })

2.2 支持的HTTP方法 #

方法 说明 示例
GET 获取资源 fastify.get()
POST 创建资源 fastify.post()
PUT 完整更新资源 fastify.put()
PATCH 部分更新资源 fastify.patch()
DELETE 删除资源 fastify.delete()
HEAD 获取响应头 fastify.head()
OPTIONS 获取支持的方法 fastify.options()
ALL 匹配所有方法 fastify.all()

2.3 多方法路由 #

javascript
fastify.route({
  method: ['GET', 'POST'],
  url: '/items',
  handler: async (request, reply) => {
    if (request.method === 'GET') {
      return { action: 'list' }
    }
    return { action: 'create' }
  }
})

三、路径参数 #

3.1 命名参数 #

javascript
fastify.get('/users/:id', async (request, reply) => {
  const { id } = request.params
  return { userId: id }
})

fastify.get('/posts/:postId/comments/:commentId', async (request, reply) => {
  const { postId, commentId } = request.params
  return { postId, commentId }
})

3.2 可选参数 #

javascript
fastify.get('/users/:id?', async (request, reply) => {
  if (request.params.id) {
    return { userId: request.params.id }
  }
  return { users: 'all users' }
})

3.3 正则参数 #

javascript
fastify.get('/users/:id(\\d+)', async (request, reply) => {
  return { userId: parseInt(request.params.id) }
})

fastify.get('/files/:filename(.*\\.txt)', async (request, reply) => {
  return { filename: request.params.filename }
})

3.4 通配符路由 #

javascript
fastify.get('/files/*', async (request, reply) => {
  return { path: request.params['*'] }
})

fastify.get('/static/**', async (request, reply) => {
  return { wildcard: request.params['**'] }
})

四、查询参数 #

4.1 获取查询参数 #

javascript
fastify.get('/search', async (request, reply) => {
  const { q, page, limit } = request.query
  return {
    query: q,
    page: page || 1,
    limit: limit || 10
  }
})

4.2 查询参数验证 #

javascript
fastify.get('/search', {
  schema: {
    querystring: {
      type: 'object',
      properties: {
        q: { type: 'string' },
        page: { type: 'integer', minimum: 1 },
        limit: { type: 'integer', minimum: 1, maximum: 100 }
      },
      required: ['q']
    }
  }
}, async (request, reply) => {
  const { q, page = 1, limit = 10 } = request.query
  return { q, page, limit }
})

五、路由选项 #

5.1 完整路由配置 #

javascript
fastify.route({
  method: 'POST',
  url: '/users',
  schema: {
    body: {
      type: 'object',
      required: ['name', 'email'],
      properties: {
        name: { type: 'string' },
        email: { type: 'string', format: 'email' }
      }
    },
    response: {
      200: {
        type: 'object',
        properties: {
          id: { type: 'integer' },
          name: { type: 'string' },
          email: { type: 'string' }
        }
      }
    }
  },
  preHandler: async (request, reply) => {
    console.log('Pre-handler')
  },
  handler: async (request, reply) => {
    return { id: 1, ...request.body }
  },
  errorHandler: (error, request, reply) => {
    reply.code(500).send({ error: 'Custom error' })
  }
})

5.2 路由选项详解 #

选项 说明
method HTTP方法
url 路由路径
schema 数据验证Schema
preHandler 请求前置处理
handler 主处理函数
preSerialization 序列化前处理
errorHandler 错误处理器
onRequest 请求钩子
onResponse 响应钩子
config 自定义配置

5.3 使用config传递配置 #

javascript
fastify.get('/users', {
  config: {
    rateLimit: {
      max: 100,
      timeWindow: '1 minute'
    },
    auth: {
      required: true
    }
  }
}, async (request, reply) => {
  return { users: [] }
})

fastify.addHook('onRequest', async (request, reply) => {
  if (request.routeConfig.auth?.required) {
    console.log('Authentication required')
  }
})

六、路由前缀 #

6.1 注册时设置前缀 #

javascript
fastify.register(async function (fastify, opts) {
  fastify.get('/', async (request, reply) => {
    return { endpoint: '/api/v1/users' }
  })
  
  fastify.get('/:id', async (request, reply) => {
    return { userId: request.params.id }
  })
}, { prefix: '/api/v1/users' })

6.2 嵌套前缀 #

javascript
fastify.register(async function (fastify, opts) {
  fastify.register(async function (fastify, opts) {
    fastify.get('/', async (request, reply) => {
      return { endpoint: '/api/v1/admin/users' }
    })
  }, { prefix: '/users' })
}, { prefix: '/api/v1/admin' })

6.3 使用fastify-plugin避免封装 #

javascript
const fp = require('fastify-plugin')

module.exports = fp(async function (fastify, opts) {
  fastify.get('/users', async (request, reply) => {
    return { users: [] }
  })
}, { prefix: '/api' })

七、路由分组 #

7.1 使用register分组 #

javascript
const userRoutes = async (fastify, opts) => {
  fastify.get('/', async (request, reply) => {
    return { users: [] }
  })
  
  fastify.post('/', async (request, reply) => {
    return { created: true }
  })
  
  fastify.get('/:id', async (request, reply) => {
    return { userId: request.params.id }
  })
}

fastify.register(userRoutes, { prefix: '/users' })

7.2 路由模块化 #

routes/users.js

javascript
module.exports = async function (fastify, opts) {
  fastify.get('/', {
    schema: {
      response: {
        200: {
          type: 'object',
          properties: {
            users: { type: 'array' }
          }
        }
      }
    }
  }, async (request, reply) => {
    return { users: [] }
  })

  fastify.post('/', async (request, reply) => {
    reply.code(201)
    return { created: true }
  })
}

app.js

javascript
fastify.register(require('./routes/users'), { prefix: '/api/users' })
fastify.register(require('./routes/products'), { prefix: '/api/products' })

八、路由约束 #

8.1 基于Host约束 #

javascript
fastify.route({
  method: 'GET',
  url: '/',
  constraints: { host: 'api.example.com' },
  handler: async (request, reply) => {
    return { message: 'API endpoint' }
  }
})

fastify.route({
  method: 'GET',
  url: '/',
  constraints: { host: 'admin.example.com' },
  handler: async (request, reply) => {
    return { message: 'Admin endpoint' }
  }
})

8.2 自定义约束 #

javascript
fastify.addConstraintStrategy({
  name: 'version',
  storage: () => {
    const store = {}
    return {
      get: (version) => store[version] || null,
      set: (version, storeValue) => { store[version] = storeValue }
    }
  },
  deriveConstraint: (req, ctx) => {
    return req.headers['x-api-version']
  },
  validate: () => true
})

fastify.route({
  method: 'GET',
  url: '/api',
  constraints: { version: 'v1' },
  handler: async (request, reply) => {
    return { version: 'v1' }
  }
})

fastify.route({
  method: 'GET',
  url: '/api',
  constraints: { version: 'v2' },
  handler: async (request, reply) => {
    return { version: 'v2' }
  }
})

九、路由查找 #

9.1 查找已注册路由 #

javascript
fastify.get('/users/:id', async (request, reply) => {
  return { userId: request.params.id }
})

const route = fastify.findRoute({
  method: 'GET',
  url: '/users/:id'
})

console.log(route.params)

9.2 打印所有路由 #

javascript
fastify.ready(err => {
  if (err) throw err
  console.log(fastify.printRoutes())
})

输出:

text
└── /
    └── users (GET, POST)
        └── /:id (GET, PUT, DELETE)

十、路由最佳实践 #

10.1 RESTful设计 #

javascript
// 获取用户列表
fastify.get('/users', async (request, reply) => {
  return { users: [] }
})

// 创建用户
fastify.post('/users', async (request, reply) => {
  reply.code(201)
  return { created: true }
})

// 获取单个用户
fastify.get('/users/:id', async (request, reply) => {
  return { userId: request.params.id }
})

// 更新用户
fastify.put('/users/:id', async (request, reply) => {
  return { updated: true }
})

// 删除用户
fastify.delete('/users/:id', async (request, reply) => {
  reply.code(204).send()
})

10.2 版本控制 #

javascript
fastify.register(require('./routes/v1'), { prefix: '/api/v1' })
fastify.register(require('./routes/v2'), { prefix: '/api/v2' })

// 或使用Header版本控制
fastify.register(require('./routes/api'), {
  constraints: { version: 'v1' }
})

10.3 路由命名 #

javascript
fastify.get('/users/:id', {
  name: 'getUser'
}, async (request, reply) => {
  return { userId: request.params.id }
})

const url = fastify.reverse('getUser', { id: 123 })
console.log(url)

十一、总结 #

本章我们学习了:

  1. 基本路由:HTTP方法、路径定义
  2. 路径参数:命名参数、可选参数、正则参数
  3. 查询参数:获取和验证
  4. 路由选项:完整配置、自定义配置
  5. 路由前缀:register、嵌套前缀
  6. 路由分组:模块化组织
  7. 路由约束:Host约束、自定义约束
  8. 最佳实践:RESTful设计、版本控制

接下来让我们学习更多路由方法!

最后更新:2026-03-28