You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

106 lines
2.6 KiB

import { match } from 'path-to-regexp';
import compose from 'koa-compose';
class Router {
/**
* 初始化路由实例
* @param {Object} options - 路由配置
* @param {string} options.prefix - 全局路由前缀
*/
constructor(options = {}) {
this.prefix = options.prefix || '';
this.routes = { get: [], post: [], put: [], delete: [] };
this.middlewares = [];
}
/**
* 注册中间件
* @param {Function} middleware - 中间件函数
*/
use(middleware) {
this.middlewares.push(middleware);
}
/**
* 注册GET路由
* @param {string} path - 路由路径
* @param {Function} handler - 处理函数
*/
get(path, handler) {
this._registerRoute('get', path, handler);
}
/**
* 注册POST路由
* @param {string} path - 路由路径
* @param {Function} handler - 处理函数
*/
post(path, handler) {
this._registerRoute('post', path, handler);
}
/**
* 创建路由组
* @param {string} prefix - 组内路由前缀
* @param {Function} callback - 组路由注册回调
*/
group(prefix, callback) {
const groupRouter = new Router({ prefix: this.prefix + prefix });
callback(groupRouter);
// 合并组路由到当前路由
Object.keys(groupRouter.routes).forEach(method => {
this.routes[method].push(...groupRouter.routes[method]);
});
this.middlewares.push(...groupRouter.middlewares);
}
/**
* 生成Koa中间件
* @returns {Function} Koa中间件函数
*/
middleware() {
return async (ctx, next) => {
const { method, path } = ctx;
const route = this._matchRoute(method.toLowerCase(), path);
// 组合所有中间件和 handler
const middlewares = [...this.middlewares];
if (route) {
ctx.params = route.params;
middlewares.push(route.handler);
}
// 用 koa-compose 组合
const composed = compose(middlewares);
await composed(ctx, next);
};
}
/**
* 内部路由注册方法
* @private
*/
_registerRoute(method, path, handler) {
const fullPath = this.prefix + path;
const keys = [];
const matcher = match(fullPath, { decode: decodeURIComponent });
this.routes[method].push({ path: fullPath, matcher, keys, handler });
}
/**
* 匹配路由
* @private
*/
_matchRoute(method, currentPath) {
const routes = this.routes[method] || [];
for (const route of routes) {
const matchResult = route.matcher(currentPath);
if (matchResult) {
return { ...route, params: matchResult.params };
}
}
return null;
}
}
export default Router;