refl: add hono Context

This commit is contained in:
yangshangbo
2025-07-07 11:27:17 +08:00
parent fc3b70349d
commit a5bf8ebc7c
8 changed files with 948 additions and 609 deletions
+256 -127
View File
@@ -1,157 +1,286 @@
# Hono - V语言Web框架 # V-Hono
一个高性能的V语言Web框架,支持混合路由和LRU缓存。 一个用 V 语言编写的轻量级 Web 框架,灵感来自 Hono.js。
## 特性 ## 特性
- 🚀 **混合路由系统**: 结合静态路径和动态路径的高效匹配 - 🚀 高性能路由系统(混合路由算法)
- ⚡ **LRU缓存**: 智能缓存路由匹配结果,大幅提升性能 - 🎯 类似 Hono.js 的 Context API
- 🎯 **向后兼容**: 保持原有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` 等,使用优化的正则表达式匹配 v run example.v
- **智能分类**: 自动识别路径类型,选择最优匹配策略
### 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
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('<h1>Hello V-Hono!</h1>')
})
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('<h1>Hello</h1>')
// 设置状态码
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 ```v
// 获取路由统计信息 // 获取路由统计信息
static_count, dynamic_count, cache_size, cache_capacity := app.get_router_stats() static_count, dynamic_count, cache_size, cache_capacity := app.get_router_stats()
println('静态路由: ${static_count}') println('静态路由: ${static_count}, 动态路由: ${dynamic_count}')
println('动态路由: ${dynamic_count}') println('缓存: ${cache_size}/${cache_capacity}')
println('缓存使用: ${cache_size}/${cache_capacity}')
```
### 缓存管理
```v
// 清理缓存 // 清理缓存
app.clear_cache() 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 ```bash
# 运行基础测试 v run example.v
v run hono/test.v
# 运行性能测试
v run hono/performance_test.v
``` ```
## 技术细节 示例包含:
- 静态路由
- 动态路由(单参数、多参数、嵌套参数)
- 所有 HTTP 方法
- 中间件功能
- 参数提取和查询参数处理
### 混合路由算法 ## 性能特性
1. **路径分类**: 根据路径是否包含特殊字符(`:`, `*`, `?`, `+`)判断类型 - **混合路由算法**:静态路由 O(1) 查找,动态路由正则匹配
2. **静态匹配**: 使用 `map[string]Handler` 进行O(1)匹配 - **LRU 缓存**:自动缓存路由匹配结果
3. **动态匹配**: 使用优化的正则表达式,支持参数提取 - **Trie 路由树**:支持复杂路径匹配
4. **缓存策略**: LRU缓存动态路由匹配结果 - **内存优化**:使用指针和堆分配优化性能
### LRU缓存实现 ## 贡献
- **双向链表**: 维护访问顺序 欢迎提交 Issue 和 Pull Request!
- **哈希表**: 快速查找缓存项
- **容量控制**: 自动淘汰最久未使用的项
- **线程安全**: 支持并发访问
## 向后兼容 ## 许可证
新版本完全兼容原有API,无需修改现有代码即可享受性能提升: MIT License
```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
+121 -71
View File
@@ -6,75 +6,79 @@ import hono
fn main() { fn main() {
mut app := hono.new_hono() mut app := hono.new_hono()
// 简单路由 - 使用基本类型 // 基本路由
app.get('/hello', fn (req hono.Request) http.Response { app.get('/hello', fn (mut c hono.Context) http.Response {
return http.Response{ return c.text('Hello, World!')
status_code: 200
body: 'Hello, World!'
}
}) })
// 静态路由 // 根路径
app.get('/', fn (req hono.Request) http.Response { app.get('/', fn (mut c hono.Context) http.Response {
return http.Response{ return c.html('<h1>Welcome to V-Hono!</h1><p>A lightweight web framework for V</p>')
status_code: 200
body: '<h1>Welcome to Hono with Hybrid Router!</h1>'
}
}) })
// 健康检查路由 // 健康检查
app.get('/api/health', fn (req hono.Request) http.Response { app.get('/api/health', fn (mut c hono.Context) http.Response {
return http.Response{ c.status(200)
status_code: 200 return c.json('{"status": "ok", "message": "Health check passed"}')
body: '{"status": "ok"}'
}
}) })
// 动态路由示例 // 动态路由 - 单参数
app.get('/users/:id', fn (req hono.Request) http.Response { app.get('/users/:id', fn (mut c hono.Context) http.Response {
return http.Response{ user_id := c.params['id']
status_code: 200 return c.json('{"user_id": "${user_id}", "name": "John Doe"}')
body: 'User route accessed, id=' + req.param['id']
}
}) })
// POST 路由示例 - 获取请求体 // 动态路由 - 多参数
app.post('/api/users', fn (req hono.Request) http.Response { app.get('/posts/:id/comments/:comment_id', fn (mut c hono.Context) http.Response {
println('收到POST请求到 /api/users') post_id := c.params['id']
println('请求体: ${req.body}') comment_id := c.params['comment_id']
println('查询参数: ${req.query}') return c.json('{"post_id": "${post_id}", "comment_id": "${comment_id}", "content": "Great post!"}')
return http.Response{
status_code: 201
body: '{"message": "User created", "body": "${req.body}"}'
}
}) })
// POST 路由示例 - 带参数的动态路由 // 动态路由 - 嵌套参数
app.post('/api/users/:id', fn (req hono.Request) http.Response { app.get('/api/users/:user_id/posts/:post_id', fn (mut c hono.Context) http.Response {
user_id := req.param['id'] user_id := c.params['user_id']
println('收到POST请求到 /api/users/${user_id}') post_id := c.params['post_id']
println('请求体: ${req.body}') return c.json('{"user_id": "${user_id}", "post_id": "${post_id}", "title": "Sample Post"}')
return http.Response{
status_code: 200
body: '{"message": "User updated", "id": "${user_id}", "body": "${req.body}"}'
}
}) })
// 综合POST示例 - 展示param、query、body // POST 请求
app.post('/api/test/:action/:id', fn (req hono.Request) http.Response { app.post('/api/users', fn (mut c hono.Context) http.Response {
action := req.param['action'] println('=== POST 请求信息 ===')
id := req.param['id'] println('请求体: ${c.body}')
println('查询参数: ${c.query}')
println('=== 综合POST请求信息 ===') c.status(201)
println('路径参数: action=${action}, id=${id}') return c.json('{"message": "User created", "data": "${c.body}"}')
println('查询参数: ${req.query}') })
println('请求体: ${req.body}')
// 带参数的 POST 请求
app.post('/api/users/:id', fn (mut c hono.Context) http.Response {
user_id := c.params['id']
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 请求
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}')
println('ID: ${id}')
println('查询参数: ${c.query}')
println('请求体: ${c.body}')
// 构建响应JSON // 构建响应JSON
mut response := '{' mut response := '{'
response += '"message": "综合测试",' response += '"message": "复杂测试",'
response += '"params": {' response += '"params": {'
response += '"action": "${action}",' response += '"action": "${action}",'
response += '"id": "${id}"' response += '"id": "${id}"'
@@ -83,7 +87,7 @@ fn main() {
// 添加查询参数 // 添加查询参数
mut query_count := 0 mut query_count := 0
for key, value in req.query { for key, value in c.query {
if query_count > 0 { if query_count > 0 {
response += ',' response += ','
} }
@@ -92,28 +96,74 @@ fn main() {
} }
response += '},' response += '},'
response += '"body": "${req.body}"' response += '"body": "${c.body}"'
response += '}' response += '}'
return http.Response{ c.status(200)
status_code: 200 return c.json(response)
body: 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)
}) })
// 日志打印中间件示例 println('Hono Server starting on :8080')
app.use(fn (mut req hono.Request, next fn (mut hono.Request) http.Response) http.Response { println('=== 测试端点 ===')
println('[LOG] ${req.url}') println('静态路由:')
return next(mut req) 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') app.listen(':8080')
} }
+94 -83
View File
@@ -3,78 +3,95 @@ module hono
import net.urllib import net.urllib
import net.http import net.http
struct Router { // Context 路由器
struct ContextRouter {
mut: mut:
handlers struct { handlers struct {
mut: mut:
get []IRequestHandler get []IHandler
post []IRequestHandler post []IHandler
put []IRequestHandler put []IHandler
delete []IRequestHandler delete []IHandler
patch []IRequestHandler patch []IHandler
head []IRequestHandler head []IHandler
options []IRequestHandler options []IHandler
} }
} }
pub struct Hono { pub struct Hono {
mut: mut:
server http.Server = http.Server{} server http.Server = http.Server{}
router Router = Router{}
routes map[string] Hono = {} routes map[string] Hono = {}
base_path string base_path string
pub mut: pub mut:
hybrid_router HybridRouter context_router ContextRouter = ContextRouter{}
trie_router TrieRouter context_hybrid_router ContextHybridRouter
middlewares []Middleware context_trie_router ContextTrieRouter
context_middlewares []ContextMiddleware
} }
pub fn (mut app Hono) get(path string, handler fn (Request) http.Response) { // Context 中间件类型
h := RequestHandler{ 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 path: path
handler: handler handler: handler
} }
app.hybrid_router.add_route('GET', h, '') app.context_hybrid_router.add_route('GET', h, '')
app.trie_router.add_route('GET', path, 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) { pub fn (mut app Hono) post(path string, handler fn (mut Context) http.Response) {
h := RequestHandler{ h := ContextHandler{
path: path path: path
handler: handler handler: handler
} }
app.hybrid_router.add_route('POST', h, '') app.context_hybrid_router.add_route('POST', h, '')
app.trie_router.add_route('POST', path, 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) { pub fn (mut app Hono) put(path string, handler fn (mut Context) http.Response) {
handlers := RequestHandler{path, handler} h := ContextHandler{path, handler}
app.router.handlers.put << handlers app.context_router.handlers.put << h
app.hybrid_router.add_route('PUT', handlers, app.base_path) 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) { pub fn (mut app Hono) delete(path string, handler fn (mut Context) http.Response) {
handlers := RequestHandler{path, handler} h := ContextHandler{path, handler}
app.router.handlers.delete << handlers app.context_router.handlers.delete << h
app.hybrid_router.add_route('DELETE', handlers, app.base_path) 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) { pub fn (mut app Hono) patch(path string, handler fn (mut Context) http.Response) {
handlers := RequestHandler{path, handler} h := ContextHandler{path, handler}
app.router.handlers.patch << handlers app.context_router.handlers.patch << h
app.hybrid_router.add_route('PATCH', handlers, app.base_path) 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) { pub fn (mut app Hono) head(path string, handler fn (mut Context) http.Response) {
handlers := RequestHandler{path, handler} h := ContextHandler{path, handler}
app.router.handlers.head << handlers app.context_router.handlers.head << h
app.hybrid_router.add_route('HEAD', handlers, app.base_path) 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) { pub fn (mut app Hono) options(path string, handler fn (mut Context) http.Response) {
handlers := RequestHandler{path, handler} h := ContextHandler{path, handler}
app.router.handlers.options << handlers app.context_router.handlers.options << h
app.hybrid_router.add_route('OPTIONS', handlers, app.base_path) 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 { 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 { fn (mut s ServerHanler) handle(req http.Request) http.Response {
mut res := http.Response{ mut res := http.Response{
status_code: 404 status_code: 404
@@ -104,7 +115,9 @@ fn (mut s ServerHanler) handle(req http.Request) http.Response {
path: '/' 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 // 解析 query
raw_query := url.query().to_map() raw_query := url.query().to_map()
mut query_map := map[string]string{} 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() param_map := route_match.params.clone()
// body // body
body := req.data body := req.data
// 构造 hono.Request // 构造 Context
mut hreq := Request{ mut ctx := Context.new(req, param_map, query_map, body)
url: url.path
param: param_map
query: query_map
body: body
}
// 洋葱模型递归执行中间件 // 洋葱模型递归执行中间件
return s.exec_middlewares(0, mut hreq, fn [route_match] (req Request) http.Response { return s.exec_context_middlewares(0, mut ctx, fn [route_match] (mut c Context) http.Response {
return route_match.handler.handle(req) return route_match.handler.handle(mut c)
}) })
} }
return res return res
} }
fn (mut s ServerHanler) exec_middlewares(idx int, mut req Request, handler fn (Request) http.Response) http.Response { // Context 版本的中间件执行函数
if idx < s.app.middlewares.len { fn (mut s ServerHanler) exec_context_middlewares(idx int, mut ctx Context, handler fn (mut Context) http.Response) http.Response {
mw := s.app.middlewares[idx] if idx < s.app.context_middlewares.len {
return mw(mut req, fn [mut s, idx, handler] (mut r Request) http.Response { mw := s.app.context_middlewares[idx]
return s.exec_middlewares(idx+1, mut r, handler) 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 { } 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 app.routes[prefix] = subapp
// 将子应用的路由添加到主应用 // 合并 Context 路由
for handler in subapp.router.handlers.get { for handler in subapp.context_router.handlers.get {
app.router.handlers.get << handler app.context_router.handlers.get << handler
} }
for handler in subapp.router.handlers.post { for handler in subapp.context_router.handlers.post {
app.router.handlers.post << handler app.context_router.handlers.post << handler
} }
for handler in subapp.router.handlers.put { for handler in subapp.context_router.handlers.put {
app.router.handlers.put << handler app.context_router.handlers.put << handler
} }
for handler in subapp.router.handlers.delete { for handler in subapp.context_router.handlers.delete {
app.router.handlers.delete << handler app.context_router.handlers.delete << handler
} }
for handler in subapp.router.handlers.patch { for handler in subapp.context_router.handlers.patch {
app.router.handlers.patch << handler app.context_router.handlers.patch << handler
} }
for handler in subapp.router.handlers.head { for handler in subapp.context_router.handlers.head {
app.router.handlers.head << handler app.context_router.handlers.head << handler
} }
for handler in subapp.router.handlers.options { for handler in subapp.context_router.handlers.options {
app.router.handlers.options << handler 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 app.base_path = base_path
} }
// 路由统计信息
pub fn (app Hono) get_router_stats() (int, int, int, int) { pub fn (app Hono) get_router_stats() (int, int, int, int) {
static_paths, dynamic_paths := app.hybrid_router.get_all_routes() static_paths, dynamic_paths := app.context_hybrid_router.get_all_routes()
cache_size, cache_capacity := app.hybrid_router.get_cache_stats() cache_size, cache_capacity := app.context_hybrid_router.get_cache_stats()
return static_paths.len, dynamic_paths.len, cache_size, cache_capacity return static_paths.len, dynamic_paths.len, cache_size, cache_capacity
} }
// 清理缓存
pub fn (mut app Hono) clear_cache() { pub fn (mut app Hono) clear_cache() {
app.hybrid_router.clear_cache() app.context_hybrid_router.clear_cache()
} }
pub fn new_hono() Hono { pub fn new_hono() Hono {
return Hono{ return Hono{
server: http.Server{} server: http.Server{}
router: Router{}
routes: map[string]Hono{} routes: map[string]Hono{}
base_path: '' base_path: ''
hybrid_router: new_hybrid_router() context_router: ContextRouter{}
trie_router: new_trie_router() context_hybrid_router: ContextHybridRouter.new()
context_trie_router: ContextTrieRouter.new()
} }
} }
+118 -45
View File
@@ -1,65 +1,138 @@
module hono module hono
// 简化的LRU缓存实现 // Context LRU 缓存节点
pub struct LRUCache { @[heap]
mut: struct ContextLRUCacheNode {
capacity int pub mut:
cache map[string]RouteMatch key string
keys []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 }
}
// ContextLRUCache 构造函数
pub fn new_lru_cache(capacity int) LRUCache { pub fn ContextLRUCache.new(capacity int) ContextLRUCache {
return LRUCache{ return ContextLRUCache{
capacity: capacity capacity: capacity
cache: map[string]RouteMatch{} size: 0
keys: []string{} cache: map[string]&ContextLRUCacheNode{}
} }
} }
pub fn (mut cache LRUCache) get(key string) ?RouteMatch { // 获取缓存值
if key in cache.cache { pub fn (mut cache ContextLRUCache) get(key string) ?ContextRouteMatch {
// 移动到列表末尾(最近使用) if mut node := cache.cache[key] {
idx := cache.keys.index(key) cache.move_to_front(mut node)
if idx != -1 { return node.value
cache.keys.delete(idx)
}
cache.keys << key
return cache.cache[key]
} }
return none return none
} }
pub fn (mut cache LRUCache) put(key string, value RouteMatch) { // 设置缓存值
if key in cache.cache { pub fn (mut cache ContextLRUCache) put(key string, value ContextRouteMatch) {
// 更新现有项 if mut node := cache.cache[key] {
cache.cache[key] = value node.value = value
// 移动到列表末尾 cache.move_to_front(mut node)
idx := cache.keys.index(key) return
if idx != -1 { }
cache.keys.delete(idx)
}
cache.keys << key
} else {
// 添加新项
cache.cache[key] = value
cache.keys << key
// 如果超出容量,删除最久未使用的项 if cache.size >= cache.capacity {
if cache.cache.len > cache.capacity { cache.remove_tail()
oldest_key := cache.keys[0] }
cache.cache.delete(oldest_key)
cache.keys.delete(0) 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() fn (mut cache ContextLRUCache) remove_tail() {
cache.keys.clear() 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
} }
+77 -16
View File
@@ -2,31 +2,92 @@ module hono
import net.http import net.http
pub struct Request { // Context 结构体,类似 Hono.js 的实现
pub struct Context {
pub: pub:
url string req http.Request
param map[string]string params map[string]string
query map[string]string query map[string]string
body string url string
pub mut:
status_code int = 200
headers map[string]string
body string
} }
// 接口定义,允许异构的泛型处理器 // Context 构造函数
pub interface IRequestHandler { 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 path string
handle(req Request) http.Response handle(mut c Context) http.Response
} }
// 泛型处理器类型 // 泛型处理器类型,使用 Context
pub type Handler = fn (Request) http.Response pub type ContextHandlerFn = fn (mut Context) http.Response
// 泛型请求处理器结构体 // Context 处理器结构体
pub struct RequestHandler { pub struct ContextHandler {
pub: pub:
path string path string
handler fn (Request) http.Response = unsafe { nil } handler fn (mut Context) http.Response = unsafe { nil }
} }
// 实现 IRequestHandler 接口 // 实现 IHandler 接口
pub fn (rh RequestHandler) handle(req Request) http.Response { pub fn (ch ContextHandler) handle(mut c Context) http.Response {
return rh.handler(req) return ch.handler(mut c)
} }
+92 -82
View File
@@ -10,39 +10,40 @@ enum RouteType {
wildcard wildcard
} }
// 路由节点 // Context 路由节点
struct RouteNode { struct ContextRouteNode {
mut: mut:
path string path string
handler IRequestHandler handler IHandler
route_type RouteType route_type RouteType
children map[string]&RouteNode children map[string]&ContextRouteNode
param_name string param_name string
base_path string base_path string
} }
// 路由匹配结果 // Context 路由匹配结果
pub struct RouteMatch { pub struct ContextRouteMatch {
pub: pub:
handler IRequestHandler handler IHandler
params map[string]string params map[string]string
path string path string
base_path string base_path string
} }
// 混合路由器 // Context 混合路由器
pub struct HybridRouter { pub struct ContextHybridRouter {
mut: mut:
static_routes map[string]IRequestHandler static_routes map[string]IHandler
dynamic_routes []IRequestHandler dynamic_routes []IHandler
cache LRUCache cache ContextLRUCache
} }
pub fn new_hybrid_router() HybridRouter { // ContextHybridRouter 构造函数
return HybridRouter{ pub fn ContextHybridRouter.new() ContextHybridRouter {
static_routes: map[string]IRequestHandler{} return ContextHybridRouter{
dynamic_routes: []IRequestHandler{} static_routes: map[string]IHandler{}
cache: new_lru_cache(1000) // 缓存1000个路由匹配结果 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('+') return path.contains(':') || path.contains('*') || path.contains('?') || path.contains('+')
} }
// 优化的正则匹配函数 // Context 版本的正则匹配函数
fn (mut router HybridRouter) match_path_with_regex(real_path string, reg_path string) (bool, regex.RE, []string) { fn (mut router ContextHybridRouter) match_path_with_regex(real_path string, reg_path string) (bool, regex.RE, []string) {
start := time.now()
// 处理多个星号 ** // 处理多个星号 **
mut one_more_star_reg := regex.regex_opt(r'\*{2,}') or { return false, regex.RE{}, []string{} } if reg_path.contains('**') {
repl_on_e_more_star_fn := fn (re regex.RE, in_txt string, start int, end int) string { // 将 ** 替换为 .*
return r'[^#\?]*' // 匹配任意字符(包括斜杠) 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'\?') if reg_path.contains('*') {
replaced_reg_path = replaced_reg_path.replace('+', r'\+') // 将 * 替换为 [^/]*
replaced_reg_path = replaced_reg_path.replace('.', r'\.') replaced_path := reg_path.replace('*', '[^/]*')
replaced_reg_path = replaced_reg_path.replace('(', r'\(') mut reg := regex.regex_opt(replaced_path) or { return false, regex.RE{}, []string{} }
replaced_reg_path = replaced_reg_path.replace(')', r'\)') return reg.matches_string(real_path), reg, []string{}
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'\|')
// 处理多个星号 // 处理参数 :param
mut replaced_path := one_more_star_reg.replace_by_fn(replaced_reg_path, repl_on_e_more_star_fn) if reg_path.contains(':') {
replaced_path = replaced_path.replace('*', r'[^/#\?]+') // 单个星号匹配单个路径段 mut replaced_path := reg_path
mut param_names := []string{}
// 提取参数名并替换为命名捕获组 // 转义特殊字符
mut param_names := []string{} 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 = replaced_path.replace('+', r'\+')
replaced_path = pamam_reg.replace_by_fn(replaced_path, fn [mut param_names] (re regex.RE, in_txt string, start int, end int) string { replaced_path = replaced_path.replace('.', r'\.')
param_name := in_txt[start+1..end] replaced_path = replaced_path.replace('(', r'\(')
param_names << param_name replaced_path = replaced_path.replace(')', r'\)')
return '(?P<' + param_name + '>[^/]+)' 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 + '$' 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 + '>[^/]+)'
})
// 编译正则表达式 // 添加结束锚点
mut reg := regex.regex_opt(replaced_path) or { return false, regex.RE{}, []string{} } replaced_path = '^' + replaced_path + '$'
end := time.now() // 编译正则表达式
mut reg := regex.regex_opt(replaced_path) or { return false, regex.RE{}, []string{} }
return reg.matches_string(real_path), reg, param_names return reg.matches_string(real_path), reg, param_names
}
return false, regex.RE{}, []string{}
} }
// 添加路由 // 添加 Context 路由
pub fn (mut router HybridRouter) add_route(method string, handler IRequestHandler, base_path string) { pub fn (mut router ContextHybridRouter) add_route(method string, handler IHandler, base_path string) {
full_path := handler.path full_path := handler.path
if is_static_path(full_path) { if is_static_path(full_path) {
router.static_routes['${method}:${full_path}'] = handler router.static_routes['${method}:${full_path}'] = handler
@@ -114,8 +124,8 @@ pub fn (mut router HybridRouter) add_route(method string, handler IRequestHandle
} }
} }
// 快速静态路径匹配 // Context 版本的静态路径匹配
fn (router HybridRouter) match_static_route(method string, path string) ?IRequestHandler { fn (router ContextHybridRouter) match_static_route(method string, path string) ?IHandler {
key := '${method}:${path}' key := '${method}:${path}'
if key in router.static_routes { if key in router.static_routes {
return router.static_routes[key] return router.static_routes[key]
@@ -123,29 +133,29 @@ fn (router HybridRouter) match_static_route(method string, path string) ?IReques
return none return none
} }
// 优化的动态路径匹配 // Context 版本的动态路径匹配
fn (mut router HybridRouter) match_dynamic_route(method string, path string) ?RouteMatch { fn (mut router ContextHybridRouter) match_dynamic_route(method string, path string) ?ContextRouteMatch {
// 先检查缓存 // 先检查缓存
cache_key := '${method}:${path}' cache_key := '${method}:${path}'
if cached := router.cache.get(cache_key) { if cached := router.cache.get(cache_key) {
return cached return cached
} }
for handler in router.dynamic_routes { for handler in router.dynamic_routes {
match_result, replaced_path_reg, param_names := router.match_path_with_regex(path, handler.path) match_result, replaced_path_reg, param_names := router.match_path_with_regex(path, handler.path)
if match_result { if match_result {
mut param_map := map[string]string{} 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) }
mut pamam_reg := regex.regex_opt(r':\w+') or { panic(err) } all_params := pamam_reg.find_all_str(handler.path)
all_params := pamam_reg.find_all_str(handler.path) for param in all_params {
for param in all_params { param_name := param[1..]
param_name := param[1..] group := replaced_path_reg.get_group_by_name(path, param_name)
group := replaced_path_reg.get_group_by_name(path, param_name) param_map[param_name] = group
param_map[param_name] = group
}
} }
route_match := RouteMatch{
route_match := ContextRouteMatch{
handler: handler handler: handler
params: param_map params: param_map
path: handler.path path: handler.path
@@ -158,11 +168,11 @@ fn (mut router HybridRouter) match_dynamic_route(method string, path string) ?Ro
return none return none
} }
// 主匹配函数 // Context 版本的主匹配函数
pub fn (mut router HybridRouter) match_route(method string, path string) ?RouteMatch { pub fn (mut router ContextHybridRouter) match_route(method string, path string) ?ContextRouteMatch {
// 1. 先尝试静态路径匹配(最快) // 1. 先尝试静态路径匹配(最快)
if static_handler := router.match_static_route(method, path) { if static_handler := router.match_static_route(method, path) {
return RouteMatch{ return ContextRouteMatch{
handler: static_handler handler: static_handler
params: map[string]string{} params: map[string]string{}
path: path 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) return router.match_dynamic_route(method, path)
} }
// 获取所有路由(用于调试) // Context 版本的获取所有路由
pub fn (router HybridRouter) get_all_routes() ([]string, []string) { pub fn (router ContextHybridRouter) get_all_routes() ([]string, []string) {
mut static_paths := []string{} mut static_paths := []string{}
mut dynamic_paths := []string{} mut dynamic_paths := []string{}
@@ -190,14 +200,14 @@ pub fn (router HybridRouter) get_all_routes() ([]string, []string) {
return static_paths, dynamic_paths 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() router.cache.clear()
} }
// 获取缓存统计信息
pub fn (router HybridRouter) get_cache_stats() (int, int) {
return router.cache.size(), router.cache.capacity
}
+110 -126
View File
@@ -1,8 +1,5 @@
module hono module hono
import regex
import net.http
// Trie 路由节点类型 // Trie 路由节点类型
enum TrieNodeType { enum TrieNodeType {
static static
@@ -10,167 +7,154 @@ enum TrieNodeType {
wildcard wildcard
} }
// Trie 路由节点 // Context Trie 路由节点
struct TrieNode { @[heap]
mut: pub struct ContextTrieNode {
pub mut:
segment string segment string
type TrieNodeType node_type TrieNodeType
param_name string param_name string
children map[string]&TrieNode handler ?ContextHandler
param_child &TrieNode children map[string]&ContextTrieNode
wildcard_child &TrieNode param_child &ContextTrieNode = unsafe { nil }
handler ?IRequestHandler wildcard_child &ContextTrieNode = unsafe { nil }
} }
pub fn new_trie_node(segment string, node_type TrieNodeType, param_name string) &TrieNode { // ContextTrieNode 构造函数
return &TrieNode{ pub fn ContextTrieNode.new(segment string, node_type TrieNodeType, param_name string) &ContextTrieNode {
return &ContextTrieNode{
segment: segment segment: segment
type: node_type node_type: node_type
param_name: param_name param_name: param_name
children: map[string]&TrieNode{} children: map[string]&ContextTrieNode{}
param_child: unsafe { nil }
wildcard_child: unsafe { nil }
} }
} }
// Trie 路由树 // Context Trie 路由器
pub struct TrieRouter { pub struct ContextTrieRouter {
mut: mut:
method_trees map[string]&TrieNode // method分流 method_trees map[string]&ContextTrieNode
regex_routes []RegexRoute cache ContextLRUCache
cache LRUCache
} }
struct RegexRoute { // ContextTrieRouter 构造函数
method string pub fn ContextTrieRouter.new() ContextTrieRouter {
pattern string return ContextTrieRouter{
handler IRequestHandler method_trees: map[string]&ContextTrieNode{}
reg regex.RE cache: ContextLRUCache.new(1000)
param_names []string
}
pub fn new_trie_router() TrieRouter {
return TrieRouter{
method_trees: map[string]&TrieNode{}
regex_routes: []RegexRoute{}
cache: new_lru_cache(1000)
} }
} }
// 注册路由 // 添加路由到 Trie 树
pub fn (mut tr TrieRouter) add_route(method string, path string, handler IRequestHandler) { pub fn (mut tr ContextTrieRouter) add_route(method string, path string, handler ContextHandler) {
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分流
if method !in tr.method_trees { 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('/') segments := path.split('/').filter(it != '')
for seg in segments { mut current := tr.method_trees[method]
for i, seg in segments {
if seg.starts_with(':') { 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 current = current.param_child
} else if seg.starts_with('*') { } else if seg == '*' {
if node.wildcard_child == unsafe { nil } { // 通配符节点
node.wildcard_child = new_trie_node(seg, TrieNodeType.wildcard, seg[1..]) if current.wildcard_child == unsafe { nil } {
current.wildcard_child = ContextTrieNode.new(seg, TrieNodeType.wildcard, seg[1..])
} }
node = node.wildcard_child current = current.wildcard_child
break // 通配符为最后一段
} else { } 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
} }
// 匹配路由 // 在 Trie 树中匹配路由
pub fn (mut tr TrieRouter) match_route(method string, path string) ?RouteMatch { pub fn (mut tr ContextTrieRouter) match_route(method string, path string) ?ContextRouteMatch {
cache_key := '${method}:${path}' // 先检查缓存
if cached := tr.cache.get(cache_key) { if cached := tr.cache.get(path) {
return cached return cached
} }
// 1. Trie 路由树 method分流
if method in tr.method_trees { if method !in tr.method_trees {
mut params := map[string]string{} return none
if handler := tr.match_trie(tr.method_trees[method] or { return none }, path, mut params) { }
route_match := RouteMatch{
handler: handler segments := path.split('/').filter(it != '')
params: params mut current := tr.method_trees[method]
path: path mut params := map[string]string{}
base_path: ''
} for seg in segments {
tr.cache.put(cache_key, route_match) // 先尝试静态匹配
return route_match 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 handler := current.handler {
if reg_route.method != method { continue } result := ContextRouteMatch{
if reg_route.reg.matches_string(path) { handler: handler
mut param_map := map[string]string{} params: params
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
} }
tr.cache.put(path, result)
return result
} }
return none return none
} }
// Trie 路由树查找(递归实现,消除mut node赋值问题) // 获取所有路由
fn (tr TrieRouter) match_trie_internal(node &TrieNode, segments []string, idx int, mut params map[string]string) ?IRequestHandler { pub fn (tr ContextTrieRouter) get_all_routes() []string {
if idx == segments.len { mut routes := []string{}
return node.handler or { return none }
for _, root in tr.method_trees {
tr.collect_routes(root, '', mut routes)
} }
seg := segments[idx]
// 优先静态节点 return routes
if seg in node.children { }
return tr.match_trie_internal(node.children[seg] or { return none }, segments, idx+1, mut params)
// 递归收集路由
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 } { if node.param_child != unsafe { nil } {
params[node.param_child.param_name] = seg tr.collect_routes(node.param_child, current_path + '/:' + node.param_child.param_name, mut routes)
return tr.match_trie_internal(node.param_child, segments, idx+1, mut params)
} }
// 最后通配符节点
if node.wildcard_child != unsafe { nil } {
params[node.wildcard_child.param_name] = segments[idx..].join('/')
return node.wildcard_child.handler or { return none }
}
return none
}
fn (tr TrieRouter) match_trie(root &TrieNode, path string, mut params map[string]string) ?IRequestHandler { // 收集通配符子节点
segments := path.trim('/').split('/') if node.wildcard_child != unsafe { nil } {
return tr.match_trie_internal(root, segments, 0, mut params) tr.collect_routes(node.wildcard_child, current_path + '/*', mut routes)
}
} }
+79 -58
View File
@@ -11,76 +11,97 @@ fn main() {
mut app := hono.new_hono() mut app := hono.new_hono()
// 添加静态路由 // 添加静态路由
app.get('/api/users', fn (req hono.Request) http.Response { app.get('/api/users', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"users": []}') return c.json('{"users": []}')
}) })
app.get('/api/posts', fn (req hono.Request) http.Response { app.get('/api/posts', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"posts": []}') return c.json('{"posts": []}')
}) })
app.get('/api/comments', fn (req hono.Request) http.Response { app.get('/api/comments', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"comments": []}') return c.json('{"comments": []}')
}) })
// 添加动态路由 // 添加动态路由
app.get('/api/users/:id', fn (req hono.Request) http.Response { app.get('/api/users/:id', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"user_id": "${req.param["id"]}"}') user_id := c.params['id']
return c.json('{"id": "${user_id}"}')
}) })
app.get('/api/posts/:id/comments', fn (req hono.Request) http.Response { app.get('/api/posts/:id/comments', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"post_id": "${req.param["id"]}", "comments": []}') post_id := c.params['id']
return c.json('{"post_id": "${post_id}", "comments": []}')
}) })
app.get('/api/**/search', fn (req hono.Request) http.Response { app.get('/api/**/search', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"search": "wildcard"}') return c.json('{"search": "wildcard"}')
}) })
// 添加100个动态路由 // 添加100个动态路由
println('添加100个动态路由...') println('添加100个动态路由...')
for i := 1; i <= 100; i++ { for i := 1; i <= 100; i++ {
// 用户相关路由 // 用户相关路由
app.get('/api/users/:id/profile', fn (req hono.Request) http.Response { app.get('/api/users/:id/profile', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"user_id": "${req.param["id"]}", "profile": "data"}') 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 { app.get('/api/users/:id/posts/:post_id', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"user_id": "${req.param["id"]}", "post_id": "${req.param["post_id"]}"}') 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 { app.get('/api/users/:id/comments/:comment_id', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"user_id": "${req.param["id"]}", "comment_id": "${req.param["comment_id"]}"}') 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 { app.get('/api/posts/:id/author/:author_id', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"post_id": "${req.param["id"]}", "author_id": "${req.param["author_id"]}"}') 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 { app.get('/api/posts/:id/tags/:tag_id', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"post_id": "${req.param["id"]}", "tag_id": "${req.param["tag_id"]}"}') 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 { app.get('/api/posts/:id/categories/:category_id', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"post_id": "${req.param["id"]}", "category_id": "${req.param["category_id"]}"}') 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 { app.get('/api/comments/:id/author/:author_id', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"comment_id": "${req.param["id"]}", "author_id": "${req.param["author_id"]}"}') 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 { app.get('/api/comments/:id/post/:post_id', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"comment_id": "${req.param["id"]}", "post_id": "${req.param["post_id"]}"}') 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 { app.get('/api/categories/:id/posts/:post_id', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"category_id": "${req.param["id"]}", "post_id": "${req.param["post_id"]}"}') 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 { app.get('/api/categories/:id/tags/:tag_id', fn (mut c hono.Context) http.Response {
return hono.Response.json('{"category_id": "${req.param["id"]}", "tag_id": "${req.param["tag_id"]}"}') 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--- 静态路由性能测试 ---') println('\n--- 静态路由性能测试 ---')
mut start := time.now() mut start := time.now()
for i := 0; i < 1000000; i++ { for i := 0; i < 1000000; i++ {
_ = app.hybrid_router.match_route('GET', '/api/users') app.context_hybrid_router.match_route('GET', '/api/users')
_ = app.hybrid_router.match_route('GET', '/api/posts') app.context_hybrid_router.match_route('GET', '/api/posts')
_ = app.hybrid_router.match_route('GET', '/api/comments') app.context_hybrid_router.match_route('GET', '/api/comments')
} }
mut end := time.now() mut end := time.now()
println('静态路由 1000000次匹配耗时: ${end - start}') println('静态路由 1000000次匹配耗时: ${end - start}')
@@ -99,13 +120,13 @@ fn main() {
println('\n--- 动态路由性能测试(103个动态路由) ---') println('\n--- 动态路由性能测试(103个动态路由) ---')
start = time.now() start = time.now()
for i := 0; i < 1000000; i++ { // 减少测试次数,因为路由数量大幅增加 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')
_ = app.hybrid_router.match_route('GET', '/api/posts/456/comments') app.context_hybrid_router.match_route('GET', '/api/posts/456/comments')
_ = app.hybrid_router.match_route('GET', '/api/anything/search') app.context_hybrid_router.match_route('GET', '/api/anything/search')
_ = app.hybrid_router.match_route('GET', '/api/users/789/profile') app.context_hybrid_router.match_route('GET', '/api/users/789/profile')
_ = app.hybrid_router.match_route('GET', '/api/posts/101/author/202') app.context_hybrid_router.match_route('GET', '/api/posts/101/author/202')
_ = app.hybrid_router.match_route('GET', '/api/comments/303/post/404') app.context_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/categories/505/tags/606')
} }
end = time.now() end = time.now()
println('动态路由 1000000次匹配耗时: ${end - start}') println('动态路由 1000000次匹配耗时: ${end - start}')
@@ -114,7 +135,7 @@ fn main() {
println('\n--- 缓存效果测试 ---') println('\n--- 缓存效果测试 ---')
start = time.now() start = time.now()
for i := 0; i < 1000000; i++ { 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() end = time.now()
println('缓存命中 1000000次匹配耗时: ${end - start}') println('缓存命中 1000000次匹配耗时: ${end - start}')
@@ -124,11 +145,11 @@ fn main() {
start = time.now() start = time.now()
for i := 0; i < 10000; i++ { for i := 0; i < 10000; i++ {
// 测试不同路由的匹配 // 测试不同路由的匹配
_ = app.hybrid_router.match_route('GET', '/api/users/${i}') app.context_hybrid_router.match_route('GET', '/api/users/${i}')
_ = app.hybrid_router.match_route('GET', '/api/posts/${i}/comments') app.context_hybrid_router.match_route('GET', '/api/posts/${i}/comments')
_ = app.hybrid_router.match_route('GET', '/api/users/${i}/profile') app.context_hybrid_router.match_route('GET', '/api/users/${i}/profile')
_ = app.hybrid_router.match_route('GET', '/api/posts/${i}/author/${i+1}') app.context_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/comments/${i}/post/${i+1}')
} }
end = time.now() end = time.now()
println('路由查找 50000次匹配耗时: ${end - start}') println('路由查找 50000次匹配耗时: ${end - start}')
@@ -144,22 +165,22 @@ fn main() {
println('\n--- Trie 路由树性能测试 ---') println('\n--- Trie 路由树性能测试 ---')
start = time.now() start = time.now()
for i := 0; i < 1000000; i++ { for i := 0; i < 1000000; i++ {
_ = app.trie_router.match_route('GET', '/api/users') app.context_trie_router.match_route('GET', '/api/users')
_ = app.trie_router.match_route('GET', '/api/posts') app.context_trie_router.match_route('GET', '/api/posts')
_ = app.trie_router.match_route('GET', '/api/comments') app.context_trie_router.match_route('GET', '/api/comments')
} }
end = time.now() end = time.now()
println('Trie静态路由 1000000次匹配耗时: ${end - start}') println('Trie静态路由 1000000次匹配耗时: ${end - start}')
start = time.now() start = time.now()
for i := 0; i < 1000000; i++ { for i := 0; i < 1000000; i++ {
_ = app.trie_router.match_route('GET', '/api/users/123') app.context_trie_router.match_route('GET', '/api/users/123')
_ = app.trie_router.match_route('GET', '/api/posts/456/comments') app.context_trie_router.match_route('GET', '/api/posts/456/comments')
_ = app.trie_router.match_route('GET', '/api/anything/search') app.context_trie_router.match_route('GET', '/api/anything/search')
_ = app.trie_router.match_route('GET', '/api/users/789/profile') app.context_trie_router.match_route('GET', '/api/users/789/profile')
_ = app.trie_router.match_route('GET', '/api/posts/101/author/202') app.context_trie_router.match_route('GET', '/api/posts/101/author/202')
_ = app.trie_router.match_route('GET', '/api/comments/303/post/404') app.context_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/categories/505/tags/606')
} }
end = time.now() end = time.now()
println('Trie动态路由 1000000次匹配耗时: ${end - start}') println('Trie动态路由 1000000次匹配耗时: ${end - start}')