2026-01-25 22:12:04 +08:00
2026-01-13 14:09:40 +08:00
2026-01-25 22:12:04 +08:00
2025-05-14 21:04:25 +08:00
2025-04-18 01:16:06 +08:00
2026-01-25 22:12:04 +08:00
2025-05-14 21:04:25 +08:00

router

一个轻量级的路由框架,支持链式调用、中间件、嵌套路由等功能。

快速开始

import { App } from '@kevisual/router';

const app = new App();
app.listen(4002);

app
  .route({ path: 'demo', key: '02' })
  .define(async (ctx) => {
    ctx.body = '02';
  })
  .addTo(app);

app
  .route({ path: 'demo', key: '03' })
  .define(async (ctx) => {
    ctx.body = '03';
  })
  .addTo(app);

核心概念

RouteContext 属性说明

在 route handler 中,你可以通过 ctx 访问以下属性:

属性 类型 说明
query object 请求参数,会自动合并 payload
body number | string | Object 响应内容
code number 响应状态码,默认为 200
message string 响应消息
state any 状态数据,可在路由间传递
appId string 应用标识
currentPath string 当前路由路径
currentKey string 当前路由 key
currentRoute Route 当前 Route 实例
progress [string, string][] 路由执行路径记录
nextQuery object 传递给下一个路由的参数
end boolean 是否提前结束路由执行
app QueryRouter 路由实例引用
error any 错误信息
index number 当前路由执行深度
needSerialize boolean 是否需要序列化响应数据

上下文方法

方法 参数 说明
ctx.call(msg, ctx?) { path, key?, payload?, ... } | { id } 调用其他路由,返回完整 context
ctx.run(msg, ctx?) { path, key?, payload? } 调用其他路由,返回 { code, data, message }
ctx.forward(res) { code, data?, message? } 设置响应结果
ctx.throw(code?, message?, tips?) - 抛出自定义错误

完整示例

import { App } from '@kevisual/router';

const app = new App();
app.listen(4002);

// 基本路由
app
  .route({ path: 'user', key: 'info' })
  .define(async (ctx) => {
    // ctx.query 包含请求参数
    const { id } = ctx.query;
    ctx.body = { id, name: '张三' };
    ctx.code = 200;
  })
  .addTo(app);

// 使用 state 在路由间传递数据
app
  .route({ path: 'order', key: 'create' })
  .define(async (ctx) => {
    ctx.state = { orderId: '12345' };
  })
  .addTo(app);

app
  .route({ path: 'order', key: 'pay' })
  .define(async (ctx) => {
    // 可以获取前一个路由设置的 state
    const { orderId } = ctx.state;
    ctx.body = { orderId, status: 'paid' };
  })
  .addTo(app);

// 链式调用
app
  .route({ path: 'product', key: 'list' })
  .define(async (ctx) => {
    ctx.body = [{ id: 1, name: '商品A' }];
  })
  .addTo(app);

// 调用其他路由
app
  .route({ path: 'dashboard', key: 'stats' })
  .define(async (ctx) => {
    // 调用 user/info 路由
    const userRes = await ctx.run({ path: 'user', key: 'info', payload: { id: 1 } });
    // 调用 product/list 路由
    const productRes = await ctx.run({ path: 'product', key: 'list' });

    ctx.body = {
      user: userRes.data,
      products: productRes.data
    };
  })
  .addTo(app);

// 使用 throw 抛出错误
app
  .route({ path: 'admin', key: 'delete' })
  .define(async (ctx) => {
    const { id } = ctx.query;
    if (!id) {
      ctx.throw(400, '缺少参数', 'id is required');
    }
    ctx.body = { success: true };
  })
  .addTo(app);

中间件

import { App, Route } from '@kevisual/router';

const app = new App();

// 定义中间件
app.route({
  id: 'auth-example',
  description: '权限校验中间件'
}).define(async(ctx) => {
  const token = ctx.query.token;
  if (!token) {
    ctx.throw(401, '未登录', '需要 token');
  }
  // 验证通过,设置用户信息到 state
  ctx.state.tokenUser = { id: 1, name: '用户A' };
}).addTo(app);

// 使用中间件(通过 id 引用)
app
  .route({ path: 'admin', key: 'panel', middleware: ['auth-example'] })
  .define(async (ctx) => {
    // 可以访问中间件设置的 state
    const { tokenUser } = ctx.state;
    ctx.body = { tokenUser };
  })
  .addTo(app);

注意事项

  1. path 和 key 的组合是路由的唯一标识,同一个 path+key 只能添加一个路由,后添加的会覆盖之前的。

  2. ctx.call vs ctx.run

    • call 返回完整 context包含所有属性
    • run 返回 { code, data, message } 格式data 即 body
  3. ctx.throw 会自动结束执行,抛出自定义错误。

  4. state 不会自动继承,每个路由的 state 是独立的,除非显式传递或使用 nextRoute。

  5. payload 会自动合并到 query,调用 ctx.run({ path, key, payload })payload 会合并到 query。

  6. nextQuery 用于传递给 nextRoute,在当前路由中设置 ctx.nextQuery,会在执行 nextRoute 时合并到 query。

  7. 避免 nextRoute 循环调用,默认最大深度为 40 次,超过会返回 500 错误。

  8. needSerialize 默认为 true,会自动对 body 进行 JSON 序列化和反序列化。

  9. progress 记录执行路径,可用于调试和追踪路由调用链。

  10. 中间件找不到会返回 404,错误信息中会包含找不到的中间件列表。

Description
No description provided
Readme 1,005 KiB
Languages
TypeScript 97.6%
JavaScript 2.4%