diff --git a/README.MD b/README.MD index 1a1d61f..65dfd1c 100644 --- a/README.MD +++ b/README.MD @@ -1,157 +1,286 @@ -# Hono - V语言Web框架 +# V-Hono -一个高性能的V语言Web框架,支持混合路由和LRU缓存。 +一个用 V 语言编写的轻量级 Web 框架,灵感来自 Hono.js。 ## 特性 -- 🚀 **混合路由系统**: 结合静态路径和动态路径的高效匹配 -- ⚡ **LRU缓存**: 智能缓存路由匹配结果,大幅提升性能 -- 🎯 **向后兼容**: 保持原有API不变,平滑升级 -- 📊 **性能监控**: 内置路由统计和缓存监控功能 +- 🚀 高性能路由系统(混合路由算法) +- 🎯 类似 Hono.js 的 Context API +- 🔧 洋葱模型中间件支持 +- 📦 内置 LRU 缓存系统 +- 🛠️ 支持动态路由参数(`:param`) +- ⚡ 支持通配符路由(`*` 和 `**`) +- 🔄 支持所有 HTTP 方法(GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS) +- 🏗️ 构造函数风格的 API 设计 -## 核心改进 +## 安装 -### 1. 混合路由系统 +```bash +# 克隆仓库 +git clone https://github.com/your-username/v-hono.git +cd v-hono -- **静态路径**: 如 `/api/users`、`/api/posts` 等,使用哈希表直接匹配,O(1)时间复杂度 -- **动态路径**: 如 `/api/users/:id`、`/api/**/search` 等,使用优化的正则表达式匹配 -- **智能分类**: 自动识别路径类型,选择最优匹配策略 - -### 2. LRU缓存 - -- **缓存容量**: 默认1000个路由匹配结果 -- **LRU策略**: 最近最少使用的缓存项会被自动淘汰 -- **缓存命中**: 重复访问相同路径时,直接从缓存返回结果 -- **性能提升**: 动态路由匹配性能提升可达80%以上 - -## 使用方法 - -### 基本路由 - -```v -import hono - -mut app := hono.Hono{} - -// 静态路由 - 直接匹配,性能最优 -app.get('/api/users', fn (req hono.Request) http.Response { - return hono.Response.json('{"users": []}') -}) - -// 动态路由 - 支持参数提取 -app.get('/api/users/:id', fn (req hono.Request) http.Response { - user_id := req.param['id'] - return hono.Response.json('{"user_id": "${user_id}"}') -}) - -// 通配符路由 - 支持 ** 模式 -app.get('/api/**/search', fn (req hono.Request) http.Response { - return hono.Response.json('{"search": "wildcard"}') -}) - -app.listen(':3000') +# 运行示例 +v run example.v ``` -### 路由统计 +## 快速开始 + +```v +module main + +import hono +import net.http + +fn main() { + mut app := hono.new_hono() + + // 基本路由 + app.get('/', fn (mut c hono.Context) http.Response { + return c.html('

Hello V-Hono!

') + }) + + app.get('/api/users/:id', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] + return c.json('{"id": "${user_id}", "name": "John Doe"}') + }) + + app.post('/api/users', fn (mut c hono.Context) http.Response { + c.status(201) + return c.json('{"message": "User created", "data": "${c.body}"}') + }) + + // 中间件 + app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { + println('[LOG] ${c.url}') + return next(mut c) + }) + + app.listen(':3000') +} +``` + +## API 参考 + +### Context + +Context 是请求的核心对象,包含所有请求和响应信息: + +```v +pub struct Context { +pub: + req http.Request // 原始 HTTP 请求 + params map[string]string // 路径参数 + query map[string]string // 查询参数 + url string // 请求 URL +pub mut: + status_code int = 200 // 响应状态码 + headers map[string]string // 响应头 + body string // 响应体 +} +``` + +### 构造函数 + +所有对象都使用构造函数风格创建: + +```v +// 创建 Context +ctx := hono.Context.new(req, params, query, body) + +// 创建路由器 +router := hono.ContextHybridRouter.new() +trie_router := hono.ContextTrieRouter.new() + +// 创建缓存 +cache := hono.ContextLRUCache.new(1000) +``` + +### 路由方法 + +支持所有 HTTP 方法: + +```v +// GET 请求 +app.get('/path', fn (mut c hono.Context) http.Response { + return c.json('{"message": "GET response"}') +}) + +// POST 请求 +app.post('/path', fn (mut c hono.Context) http.Response { + return c.json('{"message": "POST response"}') +}) + +// PUT 请求 +app.put('/path', fn (mut c hono.Context) http.Response { + return c.json('{"message": "PUT response"}') +}) + +// DELETE 请求 +app.delete('/path', fn (mut c hono.Context) http.Response { + c.status(204) + return c.text('') +}) + +// PATCH 请求 +app.patch('/path', fn (mut c hono.Context) http.Response { + return c.json('{"message": "PATCH response"}') +}) + +// HEAD 请求 +app.head('/path', fn (mut c hono.Context) http.Response { + return c.text('') +}) + +// OPTIONS 请求 +app.options('/path', fn (mut c hono.Context) http.Response { + return c.text('') +}) +``` + +### 动态路由 + +支持路径参数和通配符: + +```v +// 单参数 +app.get('/users/:id', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] + return c.json('{"id": "${user_id}"}') +}) + +// 多参数 +app.get('/posts/:id/comments/:comment_id', fn (mut c hono.Context) http.Response { + post_id := c.params['id'] + comment_id := c.params['comment_id'] + return c.json('{"post_id": "${post_id}", "comment_id": "${comment_id}"}') +}) + +// 嵌套参数 +app.get('/api/users/:user_id/posts/:post_id', fn (mut c hono.Context) http.Response { + user_id := c.params['user_id'] + post_id := c.params['post_id'] + return c.json('{"user_id": "${user_id}", "post_id": "${post_id}"}') +}) + +// 通配符 +app.get('/files/*', fn (mut c hono.Context) http.Response { + file_path := c.params['*'] + return c.text('File: ${file_path}') +}) + +// 多级通配符 +app.get('/api/**/search', fn (mut c hono.Context) http.Response { + return c.json('{"search": "wildcard"}') +}) +``` + +### 响应方法 + +所有响应方法直接返回 `http.Response`: + +```v +// JSON 响应 +return c.json('{"message": "Hello"}') + +// 文本响应 +return c.text('Hello World') + +// HTML 响应 +return c.html('

Hello

') + +// 设置状态码 +c.status(404) +return c.json('{"error": "Not Found"}') +``` + +### 中间件 + +支持洋葱模型中间件: + +```v +// 日志中间件 +app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { + println('[${time.now()}] ${c.req.method} ${c.url}') + return next(mut c) +}) + +// 认证中间件 +app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { + token := c.query['token'] + if token == '' { + c.status(401) + return c.json('{"error": "Unauthorized"}') + } + return next(mut c) +}) + +// 性能监控中间件 +app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { + start := time.now() + response := next(mut c) + duration := time.now() - start + println('请求处理时间: ${duration.milliseconds()}ms') + return response +}) +``` + +### 查询参数和请求体 + +```v +// 获取查询参数 +app.get('/search', fn (mut c hono.Context) http.Response { + query := c.query['q'] + page := c.query['page'] + return c.json('{"query": "${query}", "page": "${page}"}') +}) + +// 获取请求体 +app.post('/api/data', fn (mut c hono.Context) http.Response { + println('请求体: ${c.body}') + return c.json('{"received": "${c.body}"}') +}) +``` + +### 路由统计和缓存 ```v // 获取路由统计信息 static_count, dynamic_count, cache_size, cache_capacity := app.get_router_stats() -println('静态路由: ${static_count}') -println('动态路由: ${dynamic_count}') -println('缓存使用: ${cache_size}/${cache_capacity}') -``` +println('静态路由: ${static_count}, 动态路由: ${dynamic_count}') +println('缓存: ${cache_size}/${cache_capacity}') -### 缓存管理 - -```v // 清理缓存 app.clear_cache() - -// 获取缓存统计 -cache_size, cache_capacity := app.hybrid_router.get_cache_stats() ``` -## 性能对比 +## 示例 -| 路由类型 | 原有实现 | 混合路由 | 性能提升 | -|---------|---------|---------|---------| -| 静态路由 | 正则匹配 | 哈希表 | 95%+ | -| 动态路由 | 每次编译正则 | LRU缓存 | 80%+ | -| 通配符路由 | 每次编译正则 | LRU缓存 | 80%+ | - -## 文件结构 - -``` -hono/ -├── app.v # 主应用文件,集成混合路由 -├── router.v # 混合路由器实现 -├── cache.v # LRU缓存实现 -├── request.v # 请求结构 -├── response.v # 响应结构 -├── test.v # 基础测试 -├── performance_test.v # 性能测试 -└── README.MD # 说明文档 -``` - -## 运行测试 +运行完整示例: ```bash -# 运行基础测试 -v run hono/test.v - -# 运行性能测试 -v run hono/performance_test.v +v run example.v ``` -## 技术细节 +示例包含: +- 静态路由 +- 动态路由(单参数、多参数、嵌套参数) +- 所有 HTTP 方法 +- 中间件功能 +- 参数提取和查询参数处理 -### 混合路由算法 +## 性能特性 -1. **路径分类**: 根据路径是否包含特殊字符(`:`, `*`, `?`, `+`)判断类型 -2. **静态匹配**: 使用 `map[string]Handler` 进行O(1)匹配 -3. **动态匹配**: 使用优化的正则表达式,支持参数提取 -4. **缓存策略**: LRU缓存动态路由匹配结果 +- **混合路由算法**:静态路由 O(1) 查找,动态路由正则匹配 +- **LRU 缓存**:自动缓存路由匹配结果 +- **Trie 路由树**:支持复杂路径匹配 +- **内存优化**:使用指针和堆分配优化性能 -### LRU缓存实现 +## 贡献 -- **双向链表**: 维护访问顺序 -- **哈希表**: 快速查找缓存项 -- **容量控制**: 自动淘汰最久未使用的项 -- **线程安全**: 支持并发访问 +欢迎提交 Issue 和 Pull Request! -## 向后兼容 +## 许可证 -新版本完全兼容原有API,无需修改现有代码即可享受性能提升: - -```v -// 原有代码继续有效 -app.get('/api/users/:id', fn (req hono.Request) http.Response { - return hono.Response.json('{"user_id": "${req.param["id"]}"}') -}) -``` - -# Features -- [x] Hono App - - [x] `fn get` - - [x] `fn post` - - [x] `fn put` - - [x] `fn delete` - - [x] `fn patch` - - [x] `fn head` - - [x] `fn options` - - [x] `fn listen` -- [x] Request - - [x] `property url` - - [x] `property header` - - [x] `property query` - - [ ] `property header` - - [ ] `property data` - - [ ] `property IP` -- [x] Response - - [x] `fn text` - - [x] `fn json` - - [x] `fn html` -- [ ] Router -- [x] Middleware +MIT License diff --git a/example.v b/example.v index 23da4c4..e8e50ce 100644 --- a/example.v +++ b/example.v @@ -6,75 +6,79 @@ import hono fn main() { mut app := hono.new_hono() - // 简单路由 - 使用基本类型 - app.get('/hello', fn (req hono.Request) http.Response { - return http.Response{ - status_code: 200 - body: 'Hello, World!' - } + // 基本路由 + app.get('/hello', fn (mut c hono.Context) http.Response { + return c.text('Hello, World!') }) - // 静态路由 - app.get('/', fn (req hono.Request) http.Response { - return http.Response{ - status_code: 200 - body: '

Welcome to Hono with Hybrid Router!

' - } + // 根路径 + app.get('/', fn (mut c hono.Context) http.Response { + return c.html('

Welcome to V-Hono!

A lightweight web framework for V

') }) - // 健康检查路由 - app.get('/api/health', fn (req hono.Request) http.Response { - return http.Response{ - status_code: 200 - body: '{"status": "ok"}' - } + // 健康检查 + app.get('/api/health', fn (mut c hono.Context) http.Response { + c.status(200) + return c.json('{"status": "ok", "message": "Health check passed"}') }) - // 动态路由示例 - app.get('/users/:id', fn (req hono.Request) http.Response { - return http.Response{ - status_code: 200 - body: 'User route accessed, id=' + req.param['id'] - } + // 动态路由 - 单参数 + app.get('/users/:id', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] + return c.json('{"user_id": "${user_id}", "name": "John Doe"}') }) - // POST 路由示例 - 获取请求体 - app.post('/api/users', fn (req hono.Request) http.Response { - println('收到POST请求到 /api/users') - println('请求体: ${req.body}') - println('查询参数: ${req.query}') + // 动态路由 - 多参数 + app.get('/posts/:id/comments/:comment_id', fn (mut c hono.Context) http.Response { + post_id := c.params['id'] + comment_id := c.params['comment_id'] + return c.json('{"post_id": "${post_id}", "comment_id": "${comment_id}", "content": "Great post!"}') + }) + + // 动态路由 - 嵌套参数 + app.get('/api/users/:user_id/posts/:post_id', fn (mut c hono.Context) http.Response { + user_id := c.params['user_id'] + post_id := c.params['post_id'] + return c.json('{"user_id": "${user_id}", "post_id": "${post_id}", "title": "Sample Post"}') + }) + + // POST 请求 + app.post('/api/users', fn (mut c hono.Context) http.Response { + println('=== POST 请求信息 ===') + println('请求体: ${c.body}') + println('查询参数: ${c.query}') - return http.Response{ - status_code: 201 - body: '{"message": "User created", "body": "${req.body}"}' - } + c.status(201) + return c.json('{"message": "User created", "data": "${c.body}"}') }) - // POST 路由示例 - 带参数的动态路由 - app.post('/api/users/:id', fn (req hono.Request) http.Response { - user_id := req.param['id'] - println('收到POST请求到 /api/users/${user_id}') - println('请求体: ${req.body}') + // 带参数的 POST 请求 + app.post('/api/users/:id', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] - return http.Response{ - status_code: 200 - body: '{"message": "User updated", "id": "${user_id}", "body": "${req.body}"}' - } + println('=== 带参数的 POST 请求信息 ===') + println('用户ID: ${user_id}') + println('请求体: ${c.body}') + println('查询参数: ${c.query}') + + c.status(200) + return c.json('{"message": "User updated", "user_id": "${user_id}", "data": "${c.body}"}') }) - // 综合POST示例 - 展示param、query、body - app.post('/api/test/:action/:id', fn (req hono.Request) http.Response { - action := req.param['action'] - id := req.param['id'] + // 复杂的 POST 请求 + app.post('/api/test/:action/:id', fn (mut c hono.Context) http.Response { + action := c.params['action'] + id := c.params['id'] - println('=== 综合POST请求信息 ===') - println('路径参数: action=${action}, id=${id}') - println('查询参数: ${req.query}') - println('请求体: ${req.body}') + println('=== 复杂 POST 请求信息 ===') + println('动作: ${action}') + println('ID: ${id}') + println('查询参数: ${c.query}') + println('请求体: ${c.body}') // 构建响应JSON mut response := '{' - response += '"message": "综合测试",' + response += '"message": "复杂测试",' response += '"params": {' response += '"action": "${action}",' response += '"id": "${id}"' @@ -83,7 +87,7 @@ fn main() { // 添加查询参数 mut query_count := 0 - for key, value in req.query { + for key, value in c.query { if query_count > 0 { response += ',' } @@ -92,28 +96,74 @@ fn main() { } response += '},' - response += '"body": "${req.body}"' + response += '"body": "${c.body}"' response += '}' - return http.Response{ - status_code: 200 - body: response + c.status(200) + return c.json(response) + }) + + // PUT 请求示例 + app.put('/api/users/:id', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] + c.status(200) + return c.json('{"message": "User replaced", "user_id": "${user_id}", "data": "${c.body}"}') + }) + + // DELETE 请求示例 + app.delete('/api/users/:id', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] + c.status(204) + return c.text('') + }) + + // PATCH 请求示例 + app.patch('/api/users/:id', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] + c.status(200) + return c.json('{"message": "User partially updated", "user_id": "${user_id}", "data": "${c.body}"}') + }) + + // 中间件示例 + app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { + println('[LOG] ${c.url}') + return next(mut c) + }) + + // 认证中间件示例 + app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { + token := c.query['token'] + if token == '' { + c.status(401) + return c.json('{"error": "Unauthorized", "message": "Token required"}') } + return next(mut c) }) - // 日志打印中间件示例 - app.use(fn (mut req hono.Request, next fn (mut hono.Request) http.Response) http.Response { - println('[LOG] ${req.url}') - return next(mut req) - }) + println('Hono Server starting on :8080') + println('=== 测试端点 ===') + println('静态路由:') + println(' - GET /') + println(' - GET /hello') + println(' - GET /api/health') + println('动态路由:') + println(' - GET /users/123') + println(' - GET /posts/456/comments/789') + println(' - GET /api/users/101/posts/202') + println('POST 请求:') + println(' - POST /api/users') + println(' - POST /api/users/123') + println(' - POST /api/test/create/456') + println('其他方法:') + println(' - PUT /api/users/123') + println(' - DELETE /api/users/123') + println(' - PATCH /api/users/123') + println('') + println('测试命令示例:') + println(' curl http://localhost:8080/users/123') + println(' curl http://localhost:8080/posts/456/comments/789') + println(' curl -X POST http://localhost:8080/api/users -d "{\\"name\\":\\"John\\"}"') + println(' curl -X POST "http://localhost:8080/api/test/create/456?token=abc123" -d "{\\"name\\":\\"Test\\"}"') - println('Server starting on :8080') - println('测试静态路由: http://localhost:8080/') - println('测试健康检查: http://localhost:8080/api/health') - println('测试简单路由: http://localhost:8080/hello') - println('测试动态路由: http://localhost:8080/users/123') - println('测试POST创建用户: curl -X POST http://localhost:8080/api/users -d "{\\"name\\":\\"John\\",\\"email\\":\\"john@example.com\\"}"') - println('测试POST更新用户: curl -X POST http://localhost:8080/api/users/123 -d "{\\"name\\":\\"John Updated\\"}"') - println('测试综合POST: curl -X POST "http://localhost:8080/api/test/create/456?token=abc123&type=user" -d "{\\"name\\":\\"Test User\\",\\"email\\":\\"test@example.com\\"}"') app.listen(':8080') } \ No newline at end of file diff --git a/hono/app.v b/hono/app.v index 7259a75..9cf8208 100644 --- a/hono/app.v +++ b/hono/app.v @@ -3,78 +3,95 @@ module hono import net.urllib import net.http -struct Router { +// Context 路由器 +struct ContextRouter { mut: handlers struct { mut: - get []IRequestHandler - post []IRequestHandler - put []IRequestHandler - delete []IRequestHandler - patch []IRequestHandler - head []IRequestHandler - options []IRequestHandler + get []IHandler + post []IHandler + put []IHandler + delete []IHandler + patch []IHandler + head []IHandler + options []IHandler } } pub struct Hono { mut: server http.Server = http.Server{} - router Router = Router{} routes map[string] Hono = {} base_path string pub mut: - hybrid_router HybridRouter - trie_router TrieRouter - middlewares []Middleware + context_router ContextRouter = ContextRouter{} + context_hybrid_router ContextHybridRouter + context_trie_router ContextTrieRouter + context_middlewares []ContextMiddleware } -pub fn (mut app Hono) get(path string, handler fn (Request) http.Response) { - h := RequestHandler{ +// Context 中间件类型 +type ContextMiddleware = fn (mut c Context, next fn (mut Context) http.Response) http.Response + +// Context 接口方法 +pub fn (mut app Hono) get(path string, handler fn (mut Context) http.Response) { + h := ContextHandler{ path: path handler: handler } - app.hybrid_router.add_route('GET', h, '') - app.trie_router.add_route('GET', path, h) + app.context_hybrid_router.add_route('GET', h, '') + app.context_trie_router.add_route('GET', path, h) + app.context_router.handlers.get << h } -pub fn (mut app Hono) post(path string, handler fn (Request) http.Response) { - h := RequestHandler{ +pub fn (mut app Hono) post(path string, handler fn (mut Context) http.Response) { + h := ContextHandler{ path: path handler: handler } - app.hybrid_router.add_route('POST', h, '') - app.trie_router.add_route('POST', path, h) + app.context_hybrid_router.add_route('POST', h, '') + app.context_trie_router.add_route('POST', path, h) + app.context_router.handlers.post << h } -pub fn (mut app Hono) put(path string, handler fn (Request) http.Response) { - handlers := RequestHandler{path, handler} - app.router.handlers.put << handlers - app.hybrid_router.add_route('PUT', handlers, app.base_path) +pub fn (mut app Hono) put(path string, handler fn (mut Context) http.Response) { + h := ContextHandler{path, handler} + app.context_router.handlers.put << h + app.context_hybrid_router.add_route('PUT', h, app.base_path) + app.context_trie_router.add_route('PUT', path, h) } -pub fn (mut app Hono) delete(path string, handler fn (Request) http.Response) { - handlers := RequestHandler{path, handler} - app.router.handlers.delete << handlers - app.hybrid_router.add_route('DELETE', handlers, app.base_path) +pub fn (mut app Hono) delete(path string, handler fn (mut Context) http.Response) { + h := ContextHandler{path, handler} + app.context_router.handlers.delete << h + app.context_hybrid_router.add_route('DELETE', h, app.base_path) + app.context_trie_router.add_route('DELETE', path, h) } -pub fn (mut app Hono) patch(path string, handler fn (Request) http.Response) { - handlers := RequestHandler{path, handler} - app.router.handlers.patch << handlers - app.hybrid_router.add_route('PATCH', handlers, app.base_path) +pub fn (mut app Hono) patch(path string, handler fn (mut Context) http.Response) { + h := ContextHandler{path, handler} + app.context_router.handlers.patch << h + app.context_hybrid_router.add_route('PATCH', h, app.base_path) + app.context_trie_router.add_route('PATCH', path, h) } -pub fn (mut app Hono) head(path string, handler fn (Request) http.Response) { - handlers := RequestHandler{path, handler} - app.router.handlers.head << handlers - app.hybrid_router.add_route('HEAD', handlers, app.base_path) +pub fn (mut app Hono) head(path string, handler fn (mut Context) http.Response) { + h := ContextHandler{path, handler} + app.context_router.handlers.head << h + app.context_hybrid_router.add_route('HEAD', h, app.base_path) + app.context_trie_router.add_route('HEAD', path, h) } -pub fn (mut app Hono) options(path string, handler fn (Request) http.Response) { - handlers := RequestHandler{path, handler} - app.router.handlers.options << handlers - app.hybrid_router.add_route('OPTIONS', handlers, app.base_path) +pub fn (mut app Hono) options(path string, handler fn (mut Context) http.Response) { + h := ContextHandler{path, handler} + app.context_router.handlers.options << h + app.context_hybrid_router.add_route('OPTIONS', h, app.base_path) + app.context_trie_router.add_route('OPTIONS', path, h) +} + +// Context 中间件 +pub fn (mut app Hono) use(mw ContextMiddleware) { + app.context_middlewares << mw } struct ServerHanler { @@ -88,12 +105,6 @@ fn server_hanler_new(app Hono) ServerHanler { } } -type Middleware = fn (mut req Request, next fn (mut Request) http.Response) http.Response - -pub fn (mut app Hono) use(mw Middleware) { - app.middlewares << mw -} - fn (mut s ServerHanler) handle(req http.Request) http.Response { mut res := http.Response{ status_code: 404 @@ -104,7 +115,9 @@ fn (mut s ServerHanler) handle(req http.Request) http.Response { path: '/' } } - if route_match := s.app.hybrid_router.match_route(req.method.str(), url.path) { + + // 尝试 Context 路由 + if route_match := s.app.context_hybrid_router.match_route(req.method.str(), url.path) { // 解析 query raw_query := url.query().to_map() mut query_map := map[string]string{} @@ -117,29 +130,25 @@ fn (mut s ServerHanler) handle(req http.Request) http.Response { param_map := route_match.params.clone() // body body := req.data - // 构造 hono.Request - mut hreq := Request{ - url: url.path - param: param_map - query: query_map - body: body - } + // 构造 Context + mut ctx := Context.new(req, param_map, query_map, body) // 洋葱模型递归执行中间件 - return s.exec_middlewares(0, mut hreq, fn [route_match] (req Request) http.Response { - return route_match.handler.handle(req) + return s.exec_context_middlewares(0, mut ctx, fn [route_match] (mut c Context) http.Response { + return route_match.handler.handle(mut c) }) } return res } -fn (mut s ServerHanler) exec_middlewares(idx int, mut req Request, handler fn (Request) http.Response) http.Response { - if idx < s.app.middlewares.len { - mw := s.app.middlewares[idx] - return mw(mut req, fn [mut s, idx, handler] (mut r Request) http.Response { - return s.exec_middlewares(idx+1, mut r, handler) +// Context 版本的中间件执行函数 +fn (mut s ServerHanler) exec_context_middlewares(idx int, mut ctx Context, handler fn (mut Context) http.Response) http.Response { + if idx < s.app.context_middlewares.len { + mw := s.app.context_middlewares[idx] + return mw(mut ctx, fn [mut s, idx, handler] (mut c Context) http.Response { + return s.exec_context_middlewares(idx+1, mut c, handler) }) } else { - return handler(req) + return handler(mut ctx) } } @@ -153,27 +162,27 @@ pub fn (mut app Hono) route(prefix string, mut subapp Hono) { // 简化路由合并,直接添加所有处理器 app.routes[prefix] = subapp - // 将子应用的路由添加到主应用 - for handler in subapp.router.handlers.get { - app.router.handlers.get << handler + // 合并 Context 路由 + for handler in subapp.context_router.handlers.get { + app.context_router.handlers.get << handler } - for handler in subapp.router.handlers.post { - app.router.handlers.post << handler + for handler in subapp.context_router.handlers.post { + app.context_router.handlers.post << handler } - for handler in subapp.router.handlers.put { - app.router.handlers.put << handler + for handler in subapp.context_router.handlers.put { + app.context_router.handlers.put << handler } - for handler in subapp.router.handlers.delete { - app.router.handlers.delete << handler + for handler in subapp.context_router.handlers.delete { + app.context_router.handlers.delete << handler } - for handler in subapp.router.handlers.patch { - app.router.handlers.patch << handler + for handler in subapp.context_router.handlers.patch { + app.context_router.handlers.patch << handler } - for handler in subapp.router.handlers.head { - app.router.handlers.head << handler + for handler in subapp.context_router.handlers.head { + app.context_router.handlers.head << handler } - for handler in subapp.router.handlers.options { - app.router.handlers.options << handler + for handler in subapp.context_router.handlers.options { + app.context_router.handlers.options << handler } } @@ -181,23 +190,25 @@ pub fn (mut app Hono) set_base_path(base_path string) { app.base_path = base_path } +// 路由统计信息 pub fn (app Hono) get_router_stats() (int, int, int, int) { - static_paths, dynamic_paths := app.hybrid_router.get_all_routes() - cache_size, cache_capacity := app.hybrid_router.get_cache_stats() + static_paths, dynamic_paths := app.context_hybrid_router.get_all_routes() + cache_size, cache_capacity := app.context_hybrid_router.get_cache_stats() return static_paths.len, dynamic_paths.len, cache_size, cache_capacity } +// 清理缓存 pub fn (mut app Hono) clear_cache() { - app.hybrid_router.clear_cache() + app.context_hybrid_router.clear_cache() } pub fn new_hono() Hono { return Hono{ server: http.Server{} - router: Router{} routes: map[string]Hono{} base_path: '' - hybrid_router: new_hybrid_router() - trie_router: new_trie_router() + context_router: ContextRouter{} + context_hybrid_router: ContextHybridRouter.new() + context_trie_router: ContextTrieRouter.new() } } diff --git a/hono/cache.v b/hono/cache.v index 8f197d8..fbd6454 100644 --- a/hono/cache.v +++ b/hono/cache.v @@ -1,65 +1,138 @@ module hono -// 简化的LRU缓存实现 -pub struct LRUCache { -mut: - capacity int - cache map[string]RouteMatch - keys []string // 维护访问顺序 +// Context LRU 缓存节点 +@[heap] +struct ContextLRUCacheNode { +pub mut: + key string + value ContextRouteMatch + prev &ContextLRUCacheNode = unsafe { nil } + next &ContextLRUCacheNode = unsafe { nil } } +// Context LRU 缓存 +pub struct ContextLRUCache { +mut: + capacity int + size int + cache map[string]&ContextLRUCacheNode + head &ContextLRUCacheNode = unsafe { nil } + tail &ContextLRUCacheNode = unsafe { nil } +} - -pub fn new_lru_cache(capacity int) LRUCache { - return LRUCache{ +// ContextLRUCache 构造函数 +pub fn ContextLRUCache.new(capacity int) ContextLRUCache { + return ContextLRUCache{ capacity: capacity - cache: map[string]RouteMatch{} - keys: []string{} + size: 0 + cache: map[string]&ContextLRUCacheNode{} } } -pub fn (mut cache LRUCache) get(key string) ?RouteMatch { - if key in cache.cache { - // 移动到列表末尾(最近使用) - idx := cache.keys.index(key) - if idx != -1 { - cache.keys.delete(idx) - } - cache.keys << key - return cache.cache[key] +// 获取缓存值 +pub fn (mut cache ContextLRUCache) get(key string) ?ContextRouteMatch { + if mut node := cache.cache[key] { + cache.move_to_front(mut node) + return node.value } return none } -pub fn (mut cache LRUCache) put(key string, value RouteMatch) { - if key in cache.cache { - // 更新现有项 - cache.cache[key] = value - // 移动到列表末尾 - idx := cache.keys.index(key) - if idx != -1 { - cache.keys.delete(idx) - } - cache.keys << key - } else { - // 添加新项 - cache.cache[key] = value - cache.keys << key - - // 如果超出容量,删除最久未使用的项 - if cache.cache.len > cache.capacity { - oldest_key := cache.keys[0] - cache.cache.delete(oldest_key) - cache.keys.delete(0) - } +// 设置缓存值 +pub fn (mut cache ContextLRUCache) put(key string, value ContextRouteMatch) { + if mut node := cache.cache[key] { + node.value = value + cache.move_to_front(mut node) + return + } + + if cache.size >= cache.capacity { + cache.remove_tail() + } + + mut new_node := &ContextLRUCacheNode{ + key: key + value: value + } + + cache.cache[key] = new_node + cache.add_to_front(mut new_node) + cache.size++ +} + +// 移动到链表头部 +fn (mut cache ContextLRUCache) move_to_front(mut node ContextLRUCacheNode) { + if node == cache.head { + return + } + + // 从当前位置移除 + if node.prev != unsafe { nil } { + mut prev := node.prev + prev.next = node.next + } + if node.next != unsafe { nil } { + mut next := node.next + next.prev = node.prev + } + + // 如果是尾节点 + if node == cache.tail { + cache.tail = node.prev + } + + cache.add_to_front(mut node) +} + +// 添加到链表头部 +fn (mut cache ContextLRUCache) add_to_front(mut node ContextLRUCacheNode) { + node.next = cache.head + node.prev = unsafe { nil } + + if cache.head != unsafe { nil } { + mut head := cache.head + head.prev = node + } + + cache.head = node + + if cache.tail == unsafe { nil } { + cache.tail = node } } -pub fn (mut cache LRUCache) clear() { - cache.cache.clear() - cache.keys.clear() +// 移除链表尾部 +fn (mut cache ContextLRUCache) remove_tail() { + if cache.tail == unsafe { nil } { + return + } + + key := cache.tail.key + cache.cache.delete(key) + + if cache.head == cache.tail { + cache.head = unsafe { nil } + cache.tail = unsafe { nil } + } else { + cache.tail = cache.tail.prev + if cache.tail != unsafe { nil } { + mut tail := cache.tail + tail.next = unsafe { nil } + } + } + + cache.size-- } -pub fn (cache LRUCache) size() int { - return cache.cache.len +// 获取缓存统计信息 +pub fn (cache ContextLRUCache) get_stats() (int, int) { + return cache.size, cache.capacity +} + +// 清理缓存 +pub fn (mut cache ContextLRUCache) clear() { + cache.cache.clear() + cache.head = unsafe { nil } + cache.tail = unsafe { nil } + cache.size = 0 } \ No newline at end of file diff --git a/hono/request.v b/hono/request.v index 490b60a..eebb002 100644 --- a/hono/request.v +++ b/hono/request.v @@ -2,31 +2,92 @@ module hono import net.http -pub struct Request { +// Context 结构体,类似 Hono.js 的实现 +pub struct Context { pub: - url string - param map[string]string - query map[string]string - body string + req http.Request + params map[string]string + query map[string]string + url string +pub mut: + status_code int = 200 + headers map[string]string + body string } -// 接口定义,允许异构的泛型处理器 -pub interface IRequestHandler { +// Context 构造函数 +pub fn Context.new(req http.Request, params map[string]string, query map[string]string, body string) Context { + return Context{ + req: req + params: params + query: query + body: body + url: req.url + headers: map[string]string{} + } +} + +// Context 的便捷方法 - 直接返回 http.Response +pub fn (mut c Context) json(data string) http.Response { + mut headers := http.new_header() + headers.add_custom('Content-Type', 'application/json; charset=utf-8') or { } + for key, value in c.headers { + headers.add_custom(key, value) or { continue } + } + return http.Response{ + status_code: c.status_code + header: headers + body: data + } +} + +pub fn (mut c Context) text(data string) http.Response { + mut headers := http.new_header() + headers.add_custom('Content-Type', 'text/plain; charset=utf-8') or { } + for key, value in c.headers { + headers.add_custom(key, value) or { continue } + } + return http.Response{ + status_code: c.status_code + header: headers + body: data + } +} + +pub fn (mut c Context) html(data string) http.Response { + mut headers := http.new_header() + headers.add_custom('Content-Type', 'text/html; charset=utf-8') or { } + for key, value in c.headers { + headers.add_custom(key, value) or { continue } + } + return http.Response{ + status_code: c.status_code + header: headers + body: data + } +} + +pub fn (mut c Context) status(code int) { + c.status_code = code +} + +// 处理器接口,使用 Context +pub interface IHandler { path string - handle(req Request) http.Response + handle(mut c Context) http.Response } -// 泛型处理器类型 -pub type Handler = fn (Request) http.Response +// 泛型处理器类型,使用 Context +pub type ContextHandlerFn = fn (mut Context) http.Response -// 泛型请求处理器结构体 -pub struct RequestHandler { +// Context 处理器结构体 +pub struct ContextHandler { pub: path string - handler fn (Request) http.Response = unsafe { nil } + handler fn (mut Context) http.Response = unsafe { nil } } -// 实现 IRequestHandler 接口 -pub fn (rh RequestHandler) handle(req Request) http.Response { - return rh.handler(req) +// 实现 IHandler 接口 +pub fn (ch ContextHandler) handle(mut c Context) http.Response { + return ch.handler(mut c) } diff --git a/hono/router.v b/hono/router.v index 4043ebe..7975c15 100644 --- a/hono/router.v +++ b/hono/router.v @@ -10,39 +10,40 @@ enum RouteType { wildcard } -// 路由节点 -struct RouteNode { +// Context 路由节点 +struct ContextRouteNode { mut: path string - handler IRequestHandler + handler IHandler route_type RouteType - children map[string]&RouteNode + children map[string]&ContextRouteNode param_name string base_path string } -// 路由匹配结果 -pub struct RouteMatch { +// Context 路由匹配结果 +pub struct ContextRouteMatch { pub: - handler IRequestHandler + handler IHandler params map[string]string path string base_path string } -// 混合路由器 -pub struct HybridRouter { +// Context 混合路由器 +pub struct ContextHybridRouter { mut: - static_routes map[string]IRequestHandler - dynamic_routes []IRequestHandler - cache LRUCache + static_routes map[string]IHandler + dynamic_routes []IHandler + cache ContextLRUCache } -pub fn new_hybrid_router() HybridRouter { - return HybridRouter{ - static_routes: map[string]IRequestHandler{} - dynamic_routes: []IRequestHandler{} - cache: new_lru_cache(1000) // 缓存1000个路由匹配结果 +// ContextHybridRouter 构造函数 +pub fn ContextHybridRouter.new() ContextHybridRouter { + return ContextHybridRouter{ + static_routes: map[string]IHandler{} + dynamic_routes: []IHandler{} + cache: ContextLRUCache.new(1000) } } @@ -56,56 +57,65 @@ fn is_dynamic_path(path string) bool { return path.contains(':') || path.contains('*') || path.contains('?') || path.contains('+') } -// 优化的正则匹配函数 -fn (mut router HybridRouter) match_path_with_regex(real_path string, reg_path string) (bool, regex.RE, []string) { - start := time.now() - +// Context 版本的正则匹配函数 +fn (mut router ContextHybridRouter) match_path_with_regex(real_path string, reg_path string) (bool, regex.RE, []string) { // 处理多个星号 ** - mut one_more_star_reg := regex.regex_opt(r'\*{2,}') or { return false, regex.RE{}, []string{} } - repl_on_e_more_star_fn := fn (re regex.RE, in_txt string, start int, end int) string { - return r'[^#\?]*' // 匹配任意字符(包括斜杠) + if reg_path.contains('**') { + // 将 ** 替换为 .* + replaced_path := reg_path.replace('**', '.*') + mut reg := regex.regex_opt(replaced_path) or { return false, regex.RE{}, []string{} } + return reg.matches_string(real_path), reg, []string{} } - // 转义特殊字符 - mut replaced_reg_path := reg_path.replace('?', r'\?') - replaced_reg_path = replaced_reg_path.replace('+', r'\+') - replaced_reg_path = replaced_reg_path.replace('.', r'\.') - replaced_reg_path = replaced_reg_path.replace('(', r'\(') - replaced_reg_path = replaced_reg_path.replace(')', r'\)') - replaced_reg_path = replaced_reg_path.replace('[', r'\[') - replaced_reg_path = replaced_reg_path.replace(']', r'\]') - replaced_reg_path = replaced_reg_path.replace('{', r'\{') - replaced_reg_path = replaced_reg_path.replace('}', r'\}') - replaced_reg_path = replaced_reg_path.replace('^', r'\^') - replaced_reg_path = replaced_reg_path.replace('$', r'\$') - replaced_reg_path = replaced_reg_path.replace('|', r'\|') + // 处理单个星号 * + if reg_path.contains('*') { + // 将 * 替换为 [^/]* + replaced_path := reg_path.replace('*', '[^/]*') + mut reg := regex.regex_opt(replaced_path) or { return false, regex.RE{}, []string{} } + return reg.matches_string(real_path), reg, []string{} + } - // 处理多个星号 - mut replaced_path := one_more_star_reg.replace_by_fn(replaced_reg_path, repl_on_e_more_star_fn) - replaced_path = replaced_path.replace('*', r'[^/#\?]+') // 单个星号匹配单个路径段 - - // 提取参数名并替换为命名捕获组 - mut param_names := []string{} - mut pamam_reg := regex.regex_opt(r':[a-zA-Z_][a-zA-Z0-9_]*') or { return false, regex.RE{}, []string{} } - replaced_path = pamam_reg.replace_by_fn(replaced_path, fn [mut param_names] (re regex.RE, in_txt string, start int, end int) string { - param_name := in_txt[start+1..end] - param_names << param_name - return '(?P<' + param_name + '>[^/]+)' - }) - - // 添加结束锚点 - replaced_path = replaced_path + '$' + // 处理参数 :param + if reg_path.contains(':') { + mut replaced_path := reg_path + mut param_names := []string{} - // 编译正则表达式 - mut reg := regex.regex_opt(replaced_path) or { return false, regex.RE{}, []string{} } + // 转义特殊字符 + replaced_path = replaced_path.replace('?', r'\?') + replaced_path = replaced_path.replace('+', r'\+') + replaced_path = replaced_path.replace('.', r'\.') + replaced_path = replaced_path.replace('(', r'\(') + replaced_path = replaced_path.replace(')', r'\)') + replaced_path = replaced_path.replace('[', r'\[') + replaced_path = replaced_path.replace(']', r'\]') + replaced_path = replaced_path.replace('{', r'\{') + replaced_path = replaced_path.replace('}', r'\}') + replaced_path = replaced_path.replace('^', r'\^') + replaced_path = replaced_path.replace('$', r'\$') + replaced_path = replaced_path.replace('|', r'\|') + + // 提取参数名并替换为命名捕获组 + mut pamam_reg := regex.regex_opt(r':[a-zA-Z_][a-zA-Z0-9_]*') or { return false, regex.RE{}, []string{} } + replaced_path = pamam_reg.replace_by_fn(replaced_path, fn [mut param_names] (re regex.RE, in_txt string, start int, end int) string { + param_name := in_txt[start+1..end] + param_names << param_name + return '(?P<' + param_name + '>[^/]+)' + }) + + // 添加结束锚点 + replaced_path = '^' + replaced_path + '$' + + // 编译正则表达式 + mut reg := regex.regex_opt(replaced_path) or { return false, regex.RE{}, []string{} } + + return reg.matches_string(real_path), reg, param_names + } - end := time.now() - - return reg.matches_string(real_path), reg, param_names + return false, regex.RE{}, []string{} } -// 添加路由 -pub fn (mut router HybridRouter) add_route(method string, handler IRequestHandler, base_path string) { +// 添加 Context 路由 +pub fn (mut router ContextHybridRouter) add_route(method string, handler IHandler, base_path string) { full_path := handler.path if is_static_path(full_path) { router.static_routes['${method}:${full_path}'] = handler @@ -114,8 +124,8 @@ pub fn (mut router HybridRouter) add_route(method string, handler IRequestHandle } } -// 快速静态路径匹配 -fn (router HybridRouter) match_static_route(method string, path string) ?IRequestHandler { +// Context 版本的静态路径匹配 +fn (router ContextHybridRouter) match_static_route(method string, path string) ?IHandler { key := '${method}:${path}' if key in router.static_routes { return router.static_routes[key] @@ -123,29 +133,29 @@ fn (router HybridRouter) match_static_route(method string, path string) ?IReques return none } -// 优化的动态路径匹配 -fn (mut router HybridRouter) match_dynamic_route(method string, path string) ?RouteMatch { +// Context 版本的动态路径匹配 +fn (mut router ContextHybridRouter) match_dynamic_route(method string, path string) ?ContextRouteMatch { // 先检查缓存 cache_key := '${method}:${path}' if cached := router.cache.get(cache_key) { return cached } + for handler in router.dynamic_routes { match_result, replaced_path_reg, param_names := router.match_path_with_regex(path, handler.path) if match_result { mut param_map := map[string]string{} - start, end := replaced_path_reg.match_string(path) - if start >= 0 && end > start { - // 从原始路由路径中提取参数名 - mut pamam_reg := regex.regex_opt(r':\w+') or { panic(err) } - all_params := pamam_reg.find_all_str(handler.path) - for param in all_params { - param_name := param[1..] - group := replaced_path_reg.get_group_by_name(path, param_name) - param_map[param_name] = group - } + + // 从原始路由路径中提取参数名 + mut pamam_reg := regex.regex_opt(r':\w+') or { panic(err) } + all_params := pamam_reg.find_all_str(handler.path) + for param in all_params { + param_name := param[1..] + group := replaced_path_reg.get_group_by_name(path, param_name) + param_map[param_name] = group } - route_match := RouteMatch{ + + route_match := ContextRouteMatch{ handler: handler params: param_map path: handler.path @@ -158,11 +168,11 @@ fn (mut router HybridRouter) match_dynamic_route(method string, path string) ?Ro return none } -// 主匹配函数 -pub fn (mut router HybridRouter) match_route(method string, path string) ?RouteMatch { +// Context 版本的主匹配函数 +pub fn (mut router ContextHybridRouter) match_route(method string, path string) ?ContextRouteMatch { // 1. 先尝试静态路径匹配(最快) if static_handler := router.match_static_route(method, path) { - return RouteMatch{ + return ContextRouteMatch{ handler: static_handler params: map[string]string{} path: path @@ -174,8 +184,8 @@ pub fn (mut router HybridRouter) match_route(method string, path string) ?RouteM return router.match_dynamic_route(method, path) } -// 获取所有路由(用于调试) -pub fn (router HybridRouter) get_all_routes() ([]string, []string) { +// Context 版本的获取所有路由 +pub fn (router ContextHybridRouter) get_all_routes() ([]string, []string) { mut static_paths := []string{} mut dynamic_paths := []string{} @@ -190,14 +200,14 @@ pub fn (router HybridRouter) get_all_routes() ([]string, []string) { return static_paths, dynamic_paths } +// 获取缓存统计信息 +pub fn (router ContextHybridRouter) get_cache_stats() (int, int) { + return router.cache.get_stats() +} + // 清理缓存 -pub fn (mut router HybridRouter) clear_cache() { +pub fn (mut router ContextHybridRouter) clear_cache() { router.cache.clear() } -// 获取缓存统计信息 -pub fn (router HybridRouter) get_cache_stats() (int, int) { - return router.cache.size(), router.cache.capacity -} - \ No newline at end of file diff --git a/hono/trie_router.v b/hono/trie_router.v index 3f01454..f366911 100644 --- a/hono/trie_router.v +++ b/hono/trie_router.v @@ -1,8 +1,5 @@ module hono -import regex -import net.http - // Trie 路由节点类型 enum TrieNodeType { static @@ -10,167 +7,154 @@ enum TrieNodeType { wildcard } -// Trie 路由节点 -struct TrieNode { -mut: +// Context Trie 路由节点 +@[heap] +pub struct ContextTrieNode { +pub mut: segment string - type TrieNodeType + node_type TrieNodeType param_name string - children map[string]&TrieNode - param_child &TrieNode - wildcard_child &TrieNode - handler ?IRequestHandler + handler ?ContextHandler + children map[string]&ContextTrieNode + param_child &ContextTrieNode = unsafe { nil } + wildcard_child &ContextTrieNode = unsafe { nil } } -pub fn new_trie_node(segment string, node_type TrieNodeType, param_name string) &TrieNode { - return &TrieNode{ +// ContextTrieNode 构造函数 +pub fn ContextTrieNode.new(segment string, node_type TrieNodeType, param_name string) &ContextTrieNode { + return &ContextTrieNode{ segment: segment - type: node_type + node_type: node_type param_name: param_name - children: map[string]&TrieNode{} - param_child: unsafe { nil } - wildcard_child: unsafe { nil } + children: map[string]&ContextTrieNode{} } } -// Trie 路由树 -pub struct TrieRouter { +// Context Trie 路由器 +pub struct ContextTrieRouter { mut: - method_trees map[string]&TrieNode // method分流 - regex_routes []RegexRoute - cache LRUCache + method_trees map[string]&ContextTrieNode + cache ContextLRUCache } -struct RegexRoute { - method string - pattern string - handler IRequestHandler - reg regex.RE - param_names []string -} - -pub fn new_trie_router() TrieRouter { - return TrieRouter{ - method_trees: map[string]&TrieNode{} - regex_routes: []RegexRoute{} - cache: new_lru_cache(1000) +// ContextTrieRouter 构造函数 +pub fn ContextTrieRouter.new() ContextTrieRouter { + return ContextTrieRouter{ + method_trees: map[string]&ContextTrieNode{} + cache: ContextLRUCache.new(1000) } } -// 注册路由 -pub fn (mut tr TrieRouter) add_route(method string, path string, handler IRequestHandler) { - if path.contains('(') && path.contains(')') { - // 正则路由 - mut param_names := []string{} - mut pamam_reg := regex.regex_opt(r':([a-zA-Z_][a-zA-Z0-9_]*)') or { return } - mut replaced_path := pamam_reg.replace_by_fn(path, fn [mut param_names] (re regex.RE, in_txt string, start int, end int) string { - param_name := in_txt[start+1..end] - param_names << param_name - return '(?P<' + param_name + '>[^/]+)' - }) - reg := regex.regex_opt(replaced_path + r'$') or { return } - tr.regex_routes << RegexRoute{ - method: method - pattern: path - handler: handler - reg: reg - param_names: param_names - } - return - } - // Trie 路由 method分流 +// 添加路由到 Trie 树 +pub fn (mut tr ContextTrieRouter) add_route(method string, path string, handler ContextHandler) { if method !in tr.method_trees { - tr.method_trees[method] = new_trie_node('', TrieNodeType.static, '') + tr.method_trees[method] = ContextTrieNode.new('', TrieNodeType.static, '') } - mut node := tr.method_trees[method] or { return } - segments := path.trim('/').split('/') - for seg in segments { + + segments := path.split('/').filter(it != '') + mut current := tr.method_trees[method] + + for i, seg in segments { if seg.starts_with(':') { - if node.param_child == unsafe { nil } { - node.param_child = new_trie_node(seg, TrieNodeType.param, seg[1..]) + // 参数节点 + if current.param_child == unsafe { nil } { + current.param_child = ContextTrieNode.new(seg, TrieNodeType.param, seg[1..]) } - node = node.param_child - } else if seg.starts_with('*') { - if node.wildcard_child == unsafe { nil } { - node.wildcard_child = new_trie_node(seg, TrieNodeType.wildcard, seg[1..]) + current = current.param_child + } else if seg == '*' { + // 通配符节点 + if current.wildcard_child == unsafe { nil } { + current.wildcard_child = ContextTrieNode.new(seg, TrieNodeType.wildcard, seg[1..]) } - node = node.wildcard_child - break // 通配符为最后一段 + current = current.wildcard_child } else { - if seg !in node.children { - node.children[seg] = new_trie_node(seg, TrieNodeType.static, '') + // 静态节点 + if seg !in current.children { + current.children[seg] = ContextTrieNode.new(seg, TrieNodeType.static, '') } - node = node.children[seg] or { return } + current = current.children[seg] + } + + // 在最后一个节点设置处理器 + if i == segments.len - 1 { + current.handler = handler } } - node.handler = handler } -// 匹配路由 -pub fn (mut tr TrieRouter) match_route(method string, path string) ?RouteMatch { - cache_key := '${method}:${path}' - if cached := tr.cache.get(cache_key) { +// 在 Trie 树中匹配路由 +pub fn (mut tr ContextTrieRouter) match_route(method string, path string) ?ContextRouteMatch { + // 先检查缓存 + if cached := tr.cache.get(path) { return cached } - // 1. Trie 路由树 method分流 - if method in tr.method_trees { - mut params := map[string]string{} - if handler := tr.match_trie(tr.method_trees[method] or { return none }, path, mut params) { - route_match := RouteMatch{ - handler: handler - params: params - path: path - base_path: '' - } - tr.cache.put(cache_key, route_match) - return route_match + + if method !in tr.method_trees { + return none + } + + segments := path.split('/').filter(it != '') + mut current := tr.method_trees[method] + mut params := map[string]string{} + + for seg in segments { + // 先尝试静态匹配 + if seg in current.children { + current = current.children[seg] + } else if current.param_child != unsafe { nil } { + // 参数匹配 + params[current.param_child.param_name] = seg + current = current.param_child + } else if current.wildcard_child != unsafe { nil } { + // 通配符匹配 + current = current.wildcard_child + break + } else { + return none } } - // 2. 正则路由 - for reg_route in tr.regex_routes { - if reg_route.method != method { continue } - if reg_route.reg.matches_string(path) { - mut param_map := map[string]string{} - for param_name in reg_route.param_names { - param_map[param_name] = reg_route.reg.get_group_by_name(path, param_name) - } - route_match := RouteMatch{ - handler: reg_route.handler - params: param_map - path: reg_route.pattern - base_path: '' - } - tr.cache.put(cache_key, route_match) - return route_match + + if handler := current.handler { + result := ContextRouteMatch{ + handler: handler + params: params } + tr.cache.put(path, result) + return result } + return none } -// Trie 路由树查找(递归实现,消除mut node赋值问题) -fn (tr TrieRouter) match_trie_internal(node &TrieNode, segments []string, idx int, mut params map[string]string) ?IRequestHandler { - if idx == segments.len { - return node.handler or { return none } +// 获取所有路由 +pub fn (tr ContextTrieRouter) get_all_routes() []string { + mut routes := []string{} + + for _, root in tr.method_trees { + tr.collect_routes(root, '', mut routes) } - seg := segments[idx] - // 优先静态节点 - if seg in node.children { - return tr.match_trie_internal(node.children[seg] or { return none }, segments, idx+1, mut params) + + return routes +} + +// 递归收集路由 +fn (tr ContextTrieRouter) collect_routes(node &ContextTrieNode, current_path string, mut routes []string) { + if node.handler != none { + routes << current_path } - // 其次参数节点 + + // 收集静态子节点 + for segment, child in node.children { + tr.collect_routes(child, current_path + '/' + segment, mut routes) + } + + // 收集参数子节点 if node.param_child != unsafe { nil } { - params[node.param_child.param_name] = seg - return tr.match_trie_internal(node.param_child, segments, idx+1, mut params) + tr.collect_routes(node.param_child, current_path + '/:' + node.param_child.param_name, mut routes) } - // 最后通配符节点 + + // 收集通配符子节点 if node.wildcard_child != unsafe { nil } { - params[node.wildcard_child.param_name] = segments[idx..].join('/') - return node.wildcard_child.handler or { return none } + tr.collect_routes(node.wildcard_child, current_path + '/*', mut routes) } - return none -} - -fn (tr TrieRouter) match_trie(root &TrieNode, path string, mut params map[string]string) ?IRequestHandler { - segments := path.trim('/').split('/') - return tr.match_trie_internal(root, segments, 0, mut params) } \ No newline at end of file diff --git a/performance_test_main.v b/performance_test_main.v index 9571f18..4190966 100644 --- a/performance_test_main.v +++ b/performance_test_main.v @@ -11,76 +11,97 @@ fn main() { mut app := hono.new_hono() // 添加静态路由 - app.get('/api/users', fn (req hono.Request) http.Response { - return hono.Response.json('{"users": []}') + app.get('/api/users', fn (mut c hono.Context) http.Response { + return c.json('{"users": []}') }) - app.get('/api/posts', fn (req hono.Request) http.Response { - return hono.Response.json('{"posts": []}') + app.get('/api/posts', fn (mut c hono.Context) http.Response { + return c.json('{"posts": []}') }) - app.get('/api/comments', fn (req hono.Request) http.Response { - return hono.Response.json('{"comments": []}') + app.get('/api/comments', fn (mut c hono.Context) http.Response { + return c.json('{"comments": []}') }) // 添加动态路由 - app.get('/api/users/:id', fn (req hono.Request) http.Response { - return hono.Response.json('{"user_id": "${req.param["id"]}"}') + app.get('/api/users/:id', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] + return c.json('{"id": "${user_id}"}') }) - app.get('/api/posts/:id/comments', fn (req hono.Request) http.Response { - return hono.Response.json('{"post_id": "${req.param["id"]}", "comments": []}') + app.get('/api/posts/:id/comments', fn (mut c hono.Context) http.Response { + post_id := c.params['id'] + return c.json('{"post_id": "${post_id}", "comments": []}') }) - app.get('/api/**/search', fn (req hono.Request) http.Response { - return hono.Response.json('{"search": "wildcard"}') + app.get('/api/**/search', fn (mut c hono.Context) http.Response { + return c.json('{"search": "wildcard"}') }) // 添加100个动态路由 println('添加100个动态路由...') for i := 1; i <= 100; i++ { // 用户相关路由 - app.get('/api/users/:id/profile', fn (req hono.Request) http.Response { - return hono.Response.json('{"user_id": "${req.param["id"]}", "profile": "data"}') + app.get('/api/users/:id/profile', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] + return c.json('{"user_id": "${user_id}", "profile": {}}') }) - app.get('/api/users/:id/posts/:post_id', fn (req hono.Request) http.Response { - return hono.Response.json('{"user_id": "${req.param["id"]}", "post_id": "${req.param["post_id"]}"}') + app.get('/api/users/:id/posts/:post_id', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] + post_id := c.params['post_id'] + return c.json('{"user_id": "${user_id}", "post_id": "${post_id}"}') }) - app.get('/api/users/:id/comments/:comment_id', fn (req hono.Request) http.Response { - return hono.Response.json('{"user_id": "${req.param["id"]}", "comment_id": "${req.param["comment_id"]}"}') + app.get('/api/users/:id/comments/:comment_id', fn (mut c hono.Context) http.Response { + user_id := c.params['id'] + comment_id := c.params['comment_id'] + return c.json('{"user_id": "${user_id}", "comment_id": "${comment_id}"}') }) // 帖子相关路由 - app.get('/api/posts/:id/author/:author_id', fn (req hono.Request) http.Response { - return hono.Response.json('{"post_id": "${req.param["id"]}", "author_id": "${req.param["author_id"]}"}') + app.get('/api/posts/:id/author/:author_id', fn (mut c hono.Context) http.Response { + post_id := c.params['id'] + author_id := c.params['author_id'] + return c.json('{"post_id": "${post_id}", "author_id": "${author_id}"}') }) - app.get('/api/posts/:id/tags/:tag_id', fn (req hono.Request) http.Response { - return hono.Response.json('{"post_id": "${req.param["id"]}", "tag_id": "${req.param["tag_id"]}"}') + app.get('/api/posts/:id/tags/:tag_id', fn (mut c hono.Context) http.Response { + post_id := c.params['id'] + tag_id := c.params['tag_id'] + return c.json('{"post_id": "${post_id}", "tag_id": "${tag_id}"}') }) - app.get('/api/posts/:id/categories/:category_id', fn (req hono.Request) http.Response { - return hono.Response.json('{"post_id": "${req.param["id"]}", "category_id": "${req.param["category_id"]}"}') + app.get('/api/posts/:id/categories/:category_id', fn (mut c hono.Context) http.Response { + post_id := c.params['id'] + category_id := c.params['category_id'] + return c.json('{"post_id": "${post_id}", "category_id": "${category_id}"}') }) // 评论相关路由 - app.get('/api/comments/:id/author/:author_id', fn (req hono.Request) http.Response { - return hono.Response.json('{"comment_id": "${req.param["id"]}", "author_id": "${req.param["author_id"]}"}') + app.get('/api/comments/:id/author/:author_id', fn (mut c hono.Context) http.Response { + comment_id := c.params['id'] + author_id := c.params['author_id'] + return c.json('{"comment_id": "${comment_id}", "author_id": "${author_id}"}') }) - app.get('/api/comments/:id/post/:post_id', fn (req hono.Request) http.Response { - return hono.Response.json('{"comment_id": "${req.param["id"]}", "post_id": "${req.param["post_id"]}"}') + app.get('/api/comments/:id/post/:post_id', fn (mut c hono.Context) http.Response { + comment_id := c.params['id'] + post_id := c.params['post_id'] + return c.json('{"comment_id": "${comment_id}", "post_id": "${post_id}"}') }) // 分类相关路由 - app.get('/api/categories/:id/posts/:post_id', fn (req hono.Request) http.Response { - return hono.Response.json('{"category_id": "${req.param["id"]}", "post_id": "${req.param["post_id"]}"}') + app.get('/api/categories/:id/posts/:post_id', fn (mut c hono.Context) http.Response { + category_id := c.params['id'] + post_id := c.params['post_id'] + return c.json('{"category_id": "${category_id}", "post_id": "${post_id}"}') }) - app.get('/api/categories/:id/tags/:tag_id', fn (req hono.Request) http.Response { - return hono.Response.json('{"category_id": "${req.param["id"]}", "tag_id": "${req.param["tag_id"]}"}') + app.get('/api/categories/:id/tags/:tag_id', fn (mut c hono.Context) http.Response { + category_id := c.params['id'] + tag_id := c.params['tag_id'] + return c.json('{"category_id": "${category_id}", "tag_id": "${tag_id}"}') }) } @@ -88,9 +109,9 @@ fn main() { println('\n--- 静态路由性能测试 ---') mut start := time.now() for i := 0; i < 1000000; i++ { - _ = app.hybrid_router.match_route('GET', '/api/users') - _ = app.hybrid_router.match_route('GET', '/api/posts') - _ = app.hybrid_router.match_route('GET', '/api/comments') + app.context_hybrid_router.match_route('GET', '/api/users') + app.context_hybrid_router.match_route('GET', '/api/posts') + app.context_hybrid_router.match_route('GET', '/api/comments') } mut end := time.now() println('静态路由 1000000次匹配耗时: ${end - start}') @@ -99,13 +120,13 @@ fn main() { println('\n--- 动态路由性能测试(103个动态路由) ---') start = time.now() for i := 0; i < 1000000; i++ { // 减少测试次数,因为路由数量大幅增加 - _ = app.hybrid_router.match_route('GET', '/api/users/123') - _ = app.hybrid_router.match_route('GET', '/api/posts/456/comments') - _ = app.hybrid_router.match_route('GET', '/api/anything/search') - _ = app.hybrid_router.match_route('GET', '/api/users/789/profile') - _ = app.hybrid_router.match_route('GET', '/api/posts/101/author/202') - _ = app.hybrid_router.match_route('GET', '/api/comments/303/post/404') - _ = app.hybrid_router.match_route('GET', '/api/categories/505/tags/606') + app.context_hybrid_router.match_route('GET', '/api/users/123') + app.context_hybrid_router.match_route('GET', '/api/posts/456/comments') + app.context_hybrid_router.match_route('GET', '/api/anything/search') + app.context_hybrid_router.match_route('GET', '/api/users/789/profile') + app.context_hybrid_router.match_route('GET', '/api/posts/101/author/202') + app.context_hybrid_router.match_route('GET', '/api/comments/303/post/404') + app.context_hybrid_router.match_route('GET', '/api/categories/505/tags/606') } end = time.now() println('动态路由 1000000次匹配耗时: ${end - start}') @@ -114,7 +135,7 @@ fn main() { println('\n--- 缓存效果测试 ---') start = time.now() for i := 0; i < 1000000; i++ { - _ = app.hybrid_router.match_route('GET', '/api/users/123') + app.context_hybrid_router.match_route('GET', '/api/users/123') } end = time.now() println('缓存命中 1000000次匹配耗时: ${end - start}') @@ -124,11 +145,11 @@ fn main() { start = time.now() for i := 0; i < 10000; i++ { // 测试不同路由的匹配 - _ = app.hybrid_router.match_route('GET', '/api/users/${i}') - _ = app.hybrid_router.match_route('GET', '/api/posts/${i}/comments') - _ = app.hybrid_router.match_route('GET', '/api/users/${i}/profile') - _ = app.hybrid_router.match_route('GET', '/api/posts/${i}/author/${i+1}') - _ = app.hybrid_router.match_route('GET', '/api/comments/${i}/post/${i+1}') + app.context_hybrid_router.match_route('GET', '/api/users/${i}') + app.context_hybrid_router.match_route('GET', '/api/posts/${i}/comments') + app.context_hybrid_router.match_route('GET', '/api/users/${i}/profile') + app.context_hybrid_router.match_route('GET', '/api/posts/${i}/author/${i+1}') + app.context_hybrid_router.match_route('GET', '/api/comments/${i}/post/${i+1}') } end = time.now() println('路由查找 50000次匹配耗时: ${end - start}') @@ -144,22 +165,22 @@ fn main() { println('\n--- Trie 路由树性能测试 ---') start = time.now() for i := 0; i < 1000000; i++ { - _ = app.trie_router.match_route('GET', '/api/users') - _ = app.trie_router.match_route('GET', '/api/posts') - _ = app.trie_router.match_route('GET', '/api/comments') + app.context_trie_router.match_route('GET', '/api/users') + app.context_trie_router.match_route('GET', '/api/posts') + app.context_trie_router.match_route('GET', '/api/comments') } end = time.now() println('Trie静态路由 1000000次匹配耗时: ${end - start}') start = time.now() for i := 0; i < 1000000; i++ { - _ = app.trie_router.match_route('GET', '/api/users/123') - _ = app.trie_router.match_route('GET', '/api/posts/456/comments') - _ = app.trie_router.match_route('GET', '/api/anything/search') - _ = app.trie_router.match_route('GET', '/api/users/789/profile') - _ = app.trie_router.match_route('GET', '/api/posts/101/author/202') - _ = app.trie_router.match_route('GET', '/api/comments/303/post/404') - _ = app.trie_router.match_route('GET', '/api/categories/505/tags/606') + app.context_trie_router.match_route('GET', '/api/users/123') + app.context_trie_router.match_route('GET', '/api/posts/456/comments') + app.context_trie_router.match_route('GET', '/api/anything/search') + app.context_trie_router.match_route('GET', '/api/users/789/profile') + app.context_trie_router.match_route('GET', '/api/posts/101/author/202') + app.context_trie_router.match_route('GET', '/api/comments/303/post/404') + app.context_trie_router.match_route('GET', '/api/categories/505/tags/606') } end = time.now() println('Trie动态路由 1000000次匹配耗时: ${end - start}')