refc: Refactor project structure

This commit is contained in:
meiseayoung
2025-12-30 11:00:46 +08:00
parent 369655a9ea
commit 782a4e7795
106 changed files with 947 additions and 4184 deletions
+145
View File
@@ -0,0 +1,145 @@
module main
import hono
import net.http
import json
fn main() {
// 创建数据库管理器
db_manager := hono.new_database_manager('auth_system.db') or {
eprintln('Failed to create database manager: $err')
return
}
// 创建认证管理器
mut auth_manager := hono.new_auth_manager(db_manager)
// 初始化认证相关表
auth_manager.init_tables() or {
eprintln('Failed to initialize auth tables: $err')
return
}
// 创建 Hono 应用
mut app := hono.Hono{}
// 注册认证路由
hono.register_auth_routes(mut app, mut auth_manager)
// 添加一些示例菜单项
create_sample_menus(mut auth_manager)
// 添加一些示例用户
create_sample_users(mut auth_manager)
// 添加一些受保护的路由示例
app.get('/api/protected/user', fn [auth_manager] (mut c hono.Context) http.Response {
token := c.req.header.get_custom('Authorization') or { '' }
user := auth_manager.verify_token(token) or {
c.status(401)
return c.json(json.encode({
'error': 'User not found'
}))
}
return c.json(json.encode({
'message': 'This is a protected user route',
'user_id': user.id.str(),
'username': user.username,
'email': user.email,
'role': user.role.str()
}))
})
app.get('/api/protected/admin', fn [auth_manager] (mut c hono.Context) http.Response {
token := c.req.header.get_custom('Authorization') or { '' }
user := auth_manager.verify_token(token) or {
c.status(401)
return c.json(json.encode({
'error': 'User not found'
}))
}
if !auth_manager.check_permission(user, 'manage') {
c.status(403)
return c.json(json.encode({
'error': 'Admin access required'
}))
}
return c.json(json.encode({
'message': 'This is a protected admin route',
'user_id': user.id.str(),
'username': user.username,
'email': user.email,
'role': user.role.str()
}))
})
// 添加静态文件服务
app.get('/', fn (mut c hono.Context) http.Response {
return c.file('public/auth.html')
})
println('🚀 用户角色菜单管理系统已启动')
println('📱 访问地址: http://localhost:3000')
println('')
println('📋 示例用户:')
println(' 用户名: admin, 密码: admin123, 角色: admin')
println(' 用户名: manager, 密码: manager123, 角色: manager')
println(' 用户名: user, 密码: user123, 角色: user')
println(' 用户名: guest, 密码: guest123, 角色: guest')
println('')
// 启动服务器
app.listen(':3000')
}
// 创建示例菜单
fn create_sample_menus(mut auth_manager hono.AuthManager) {
// 创建根菜单
auth_manager.create_menu_item('仪表板', '/dashboard', '📊', 0, 1, ['read']) or { println('仪表板插入失败') }
auth_manager.create_menu_item('用户管理', '/users', '👥', 0, 2, ['read', 'write', 'manage']) or { println('用户管理插入失败') }
auth_manager.create_menu_item('系统设置', '/settings', '⚙️', 0, 3, ['manage']) or { println('系统设置插入失败') }
auth_manager.create_menu_item('文件管理', '/files', '📁', 0, 4, ['read', 'write']) or { println('文件管理插入失败') }
// 获取用户管理菜单ID(动态获取)
user_menu_id := auth_manager.get_menu_id_by_path('/users')
// 创建子菜单
auth_manager.create_menu_item('用户列表', '/users/list', '📋', user_menu_id, 1, ['read']) or { println('用户列表插入失败') }
auth_manager.create_menu_item('添加用户', '/users/add', '➕', user_menu_id, 2, ['write']) or { println('添加用户插入失败') }
auth_manager.create_menu_item('编辑用户', '/users/edit', '✏️', user_menu_id, 3, ['write']) or { println('编辑用户插入失败') }
auth_manager.create_menu_item('删除用户', '/users/delete', '🗑️', user_menu_id, 4, ['manage']) or { println('删除用户插入失败') }
// 更多测试菜单
auth_manager.create_menu_item('报表中心', '/reports', '📈', 0, 5, ['read', 'write']) or { println('报表中心插入失败') }
reports_menu_id := auth_manager.get_menu_id_by_path('/reports')
auth_manager.create_menu_item('销售报表', '/reports/sales', '💹', reports_menu_id, 1, ['read']) or { println('销售报表插入失败') }
auth_manager.create_menu_item('财务报表', '/reports/finance', '💰', reports_menu_id, 2, ['read']) or { println('财务报表插入失败') }
auth_manager.create_menu_item('消息中心', '/messages', '📨', 0, 6, ['read']) or { println('消息中心插入失败') }
auth_manager.create_menu_item('个人中心', '/profile', '👤', 0, 7, ['read', 'write']) or { println('个人中心插入失败') }
auth_manager.create_menu_item('测试菜单A', '/test/a', '🅰️', 0, 8, ['read']) or { println('测试菜单A插入失败') }
auth_manager.create_menu_item('测试菜单B', '/test/b', '🅱️', 0, 9, ['read']) or { println('测试菜单B插入失败') }
auth_manager.create_menu_item('测试菜单C', '/test/c', '🆑', 0, 10, ['read']) or { println('测试菜单C插入失败') }
test_a_id := auth_manager.get_menu_id_by_path('/test/a')
auth_manager.create_menu_item('A-子菜单1', '/test/a/1', '1️⃣', test_a_id, 1, ['read']) or { println('A-子菜单1插入失败') }
auth_manager.create_menu_item('A-子菜单2', '/test/a/2', '2️⃣', test_a_id, 2, ['read']) or { println('A-子菜单2插入失败') }
println('✅ 示例菜单已创建')
println('✅ 更多测试菜单已创建')
}
// 创建示例用户
fn create_sample_users(mut auth_manager hono.AuthManager) {
// 创建管理员用户
auth_manager.create_user('admin', 'admin@example.com', 'admin123', hono.UserRole.admin) or { return }
// 创建经理用户
auth_manager.create_user('manager', 'manager@example.com', 'manager123', hono.UserRole.manager) or { return }
// 创建普通用户
auth_manager.create_user('user', 'user@example.com', 'user123', hono.UserRole.user) or { return }
// 创建访客用户
auth_manager.create_user('guest', 'guest@example.com', 'guest123', hono.UserRole.guest) or { return }
println('✅ 示例用户已创建')
}
+47
View File
@@ -0,0 +1,47 @@
// Basic example of using v-hono web framework
// Run with: v run examples/basic/example.v
// Or after installing: v install your-username.hono
module main
import net.http
import hono
fn main() {
mut app := hono.Hono.new()
// Basic GET route
app.get('/', fn (mut c hono.Context) http.Response {
return c.text('Hello, World!')
})
// JSON response
app.get('/json', fn (mut c hono.Context) http.Response {
return c.json('{"message": "Hello, JSON!"}')
})
// HTML response
app.get('/html', fn (mut c hono.Context) http.Response {
return c.html('<h1>Hello, HTML!</h1>')
})
// Route with parameters
app.get('/users/:id', fn (mut c hono.Context) http.Response {
user_id := c.params['id'] or { 'unknown' }
return c.json('{"user_id": "${user_id}"}')
})
// Query parameters
app.get('/search', fn (mut c hono.Context) http.Response {
query := c.query['q'] or { '' }
return c.json('{"query": "${query}"}')
})
// POST route
app.post('/api/data', fn (mut c hono.Context) http.Response {
body := c.body
return c.json('{"received": "${body}"}')
})
println('Server starting on http://localhost:3000')
app.listen(':3000')
}
+86
View File
@@ -0,0 +1,86 @@
import hono
import time
fn main() {
println('=== 简化LRU缓存测试 ===')
// 创建简单的路由匹配对象
test_handler := hono.ContextHandler{
path: '/test'
handler: unsafe { nil } // 简化,不设置真实handler
}
test_route_match := hono.ContextRouteMatch{
handler: test_handler
params: map[string]string{}
path: '/test'
base_path: ''
}
// 测试基本功能
println('测试1: 创建缓存并添加项目')
mut cache := hono.ContextLRUCache.new(3)
cache.put('key1', test_route_match)
cache.put('key2', test_route_match)
size, capacity := cache.get_stats()
println('添加2项后: $size/$capacity')
// 测试详细统计
println('\n测试2: 获取详细统计信息')
stats := cache.get_detailed_stats()
println('大小: ${stats['size']}')
println('容量: ${stats['capacity']}')
println('TTL: ${stats['ttl_seconds']}秒')
// 测试健康检查
println('\n测试3: 健康检查')
is_healthy := cache.is_healthy()
println('缓存健康状态: $is_healthy')
// 测试TTL设置
println('\n测试4: TTL设置')
cache.set_ttl(10)
cache.set_cleanup_interval(5)
// 测试强制清理
println('\n测试5: 强制清理过期项')
cache.force_cleanup_expired()
size2, _ := cache.get_stats()
println('强制清理后大小: $size2')
// 测试完全清理
println('\n测试6: 完全清理')
cache.clear()
size3, _ := cache.get_stats()
is_healthy2 := cache.is_healthy()
println('完全清理后大小: $size3')
println('清理后健康状态: $is_healthy2')
// 测试TTL缓存
println('\n测试7: TTL功能')
mut ttl_cache := hono.ContextLRUCache.new_with_ttl(5, 2) // 2秒TTL
ttl_cache.put('expire_key', test_route_match)
println('添加会过期的项目')
// 立即检查
if _ := ttl_cache.get('expire_key') {
println('立即获取: ✅ 成功')
} else {
println('立即获取: ❌ 失败')
}
// 等待过期
println('等待3秒让项目过期...')
time.sleep(3 * time.second)
if _ := ttl_cache.get('expire_key') {
println('过期后获取: ❌ 仍然存在')
} else {
println('过期后获取: ✅ 正确过期')
}
println('\n所有测试完成! 🎉')
}
+54
View File
@@ -0,0 +1,54 @@
{
"server": {
"host": "127.0.0.1",
"port": 8080,
"read_timeout": 30,
"write_timeout": 30,
"max_request_size": 10485760,
"enable_cors": true,
"enable_gzip": true
},
"static": {
"enabled": true,
"root_dir": "./static",
"index_files": ["index.html", "index.htm"],
"cache_max_age": 3600,
"enable_directory_listing": false,
"allowed_extensions": [".html", ".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".txt", ".pdf"]
},
"upload": {
"enabled": true,
"upload_dir": "./uploads",
"max_file_size": 104857600,
"max_chunk_size": 5242880,
"merge_buffer_size": 8192,
"allowed_types": [".txt", ".pdf", ".doc", ".docx", ".jpg", ".jpeg", ".png", ".gif"],
"cleanup_timeout": 3600
},
"cache": {
"enabled": true,
"max_size": 1000,
"default_ttl": 300,
"cleanup_interval": 60
},
"security": {
"enable_path_validation": true,
"enable_file_type_check": true,
"enable_size_limits": true,
"max_path_length": 255,
"blocked_extensions": [".exe", ".bat", ".cmd", ".sh", ".php", ".asp", ".jsp"],
"enable_xss_protection": true,
"enable_csrf_protection": false
},
"log": {
"enabled": true,
"level": "info",
"output": "console",
"file_path": "./logs/app.log",
"max_file_size": 10485760,
"max_backup_files": 5,
"enable_request_log": true
},
"debug": false,
"env": "development"
}
+39
View File
@@ -0,0 +1,39 @@
// Middleware example for v-hono web framework
module main
import net.http
import time
import hono
fn main() {
mut app := hono.Hono.new()
// Logger middleware
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.since(start)
println('[${c.req.method}] ${c.path} - ${duration}')
return response
})
// CORS middleware
app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
c.headers['Access-Control-Allow-Origin'] = '*'
c.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
c.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return next(mut c)
})
// Routes
app.get('/', fn (mut c hono.Context) http.Response {
return c.text('Hello with middleware!')
})
app.get('/api/data', fn (mut c hono.Context) http.Response {
return c.json('{"data": "some data"}')
})
println('Server starting on http://localhost:3000')
app.listen(':3000')
}
+41
View File
@@ -0,0 +1,41 @@
// Route grouping example for v-hono web framework
module main
import net.http
import hono
fn main() {
mut app := hono.Hono.new()
// Create API sub-application
mut api := hono.Hono.new()
api.get('/users', fn (mut c hono.Context) http.Response {
return c.json('[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]')
})
api.get('/users/:id', fn (mut c hono.Context) http.Response {
user_id := c.params['id'] or { 'unknown' }
return c.json('{"id": ${user_id}, "name": "User ${user_id}"}')
})
api.post('/users', fn (mut c hono.Context) http.Response {
return c.json('{"message": "User created"}')
})
// Mount API routes under /api prefix
app.route('/api', mut api)
// Root route
app.get('/', fn (mut c hono.Context) http.Response {
return c.html('<h1>Welcome to v-hono!</h1><p>API available at /api</p>')
})
println('Server starting on http://localhost:3000')
println('Routes:')
println(' GET / - Welcome page')
println(' GET /api/users - List users')
println(' GET /api/users/:id - Get user by ID')
println(' POST /api/users - Create user')
app.listen(':3000')
}
@@ -0,0 +1,235 @@
// V-Hono 路由分组示例
// 参考 Hono.js 的路由分组功能: https://hono.dev/docs/api/routing#grouping
//
// Hono.js 路由分组示例:
// ```typescript
// const book = new Hono()
// book.get('/', (c) => c.text('List Books'))
// book.get('/:id', (c) => c.text('Get Book: ' + c.req.param('id')))
//
// const app = new Hono()
// app.route('/books', book)
// ```
import net.http
import hono
fn main() {
println('🚀 V-Hono 路由分组示例启动中...')
// 创建主应用
mut app := hono.Hono.new()
// 添加全局日志中间件
app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
println('[LOG] ${c.req.method} ${c.path}')
return next(mut c)
})
// 根路由
app.get('/', fn (mut c hono.Context) http.Response {
return c.html(generate_index_page())
})
// ========================================
// 路由分组: Books API - /api/books
// ========================================
mut books := hono.Hono.new()
// 在子应用中定义相对路径,挂载时会自动添加前缀
books.get('/', fn (mut c hono.Context) http.Response {
return c.json('[{"id": 1, "title": "V Programming"}, {"id": 2, "title": "Web Development"}]')
})
books.get('/:id', fn (mut c hono.Context) http.Response {
book_id := c.params['id']
return c.json('{"id": "${book_id}", "title": "V Programming", "author": "V Team"}')
})
books.post('/', fn (mut c hono.Context) http.Response {
c.status(201)
return c.json('{"message": "Book created", "body": "${c.body}"}')
})
books.put('/:id', fn (mut c hono.Context) http.Response {
book_id := c.params['id']
return c.json('{"message": "Book updated", "id": "${book_id}"}')
})
books.delete('/:id', fn (mut c hono.Context) http.Response {
c.status(204)
return c.text('')
})
// 挂载 books 路由组到 /api/books
app.route('/api/books', mut books)
// ========================================
// 路由分组: Users API - /api/users
// ========================================
mut users := hono.Hono.new()
users.get('/', fn (mut c hono.Context) http.Response {
return c.json('[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]')
})
users.get('/:id', fn (mut c hono.Context) http.Response {
user_id := c.params['id']
return c.json('{"id": "${user_id}", "name": "Alice", "email": "alice@example.com"}')
})
users.get('/:user_id/posts', fn (mut c hono.Context) http.Response {
user_id := c.params['user_id']
return c.json('[{"id": 1, "user_id": "${user_id}", "title": "My First Post"}]')
})
users.get('/: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('{"id": "${post_id}", "user_id": "${user_id}", "title": "My Post"}')
})
users.post('/', fn (mut c hono.Context) http.Response {
c.status(201)
return c.json('{"message": "User created", "body": "${c.body}"}')
})
// 挂载 users 路由组到 /api/users
app.route('/api/users', mut users)
// ========================================
// 路由分组: Admin API - /admin
// ========================================
mut admin := hono.Hono.new()
admin.get('/', fn (mut c hono.Context) http.Response {
return c.json('{"page": "Admin Dashboard", "stats": {"users": 100, "books": 50}}')
})
admin.get('/users', fn (mut c hono.Context) http.Response {
return c.json('[{"id": 1, "name": "Admin User", "role": "admin"}]')
})
admin.get('/settings', fn (mut c hono.Context) http.Response {
return c.json('{"theme": "dark", "language": "zh-CN"}')
})
// 挂载 admin 路由组到 /admin
app.route('/admin', mut admin)
// ========================================
// 其他路由(直接在主应用定义)
// ========================================
app.get('/health', fn (mut c hono.Context) http.Response {
return c.json('{"status": "ok"}')
})
app.get('/api/version', fn (mut c hono.Context) http.Response {
return c.json('{"version": "1.0.0", "name": "V-Hono API"}')
})
// 打印路由信息
print_routes_info()
// 启动服务器
app.listen(':8080')
}
// 生成首页 HTML
fn generate_index_page() string {
return '<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>V-Hono 路由分组示例</title>
<style>
body { font-family: Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; }
.group { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 8px; }
.endpoint { background: #f5f5f5; padding: 10px; margin: 5px 0; border-radius: 4px; font-family: monospace; }
.method { display: inline-block; width: 60px; font-weight: bold; }
.get { color: #61affe; }
.post { color: #49cc90; }
.put { color: #fca130; }
.delete { color: #f93e3e; }
h2 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; }
pre { background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 5px; overflow-x: auto; }
</style>
</head>
<body>
<h1>🚀 V-Hono 路由分组示例</h1>
<p>参考 <a href="https://hono.dev/docs/api/routing#grouping">Hono.js 路由分组</a> 实现</p>
<div class="group">
<h2>📚 Books API (/api/books)</h2>
<div class="endpoint"><span class="method get">GET</span> <a href="/api/books">/api/books</a> - 获取所有书籍</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/api/books/1">/api/books/:id</a> - 获取单本书籍</div>
<div class="endpoint"><span class="method post">POST</span> /api/books - 创建书籍</div>
<div class="endpoint"><span class="method put">PUT</span> /api/books/:id - 更新书籍</div>
<div class="endpoint"><span class="method delete">DELETE</span> /api/books/:id - 删除书籍</div>
</div>
<div class="group">
<h2>👥 Users API (/api/users)</h2>
<div class="endpoint"><span class="method get">GET</span> <a href="/api/users">/api/users</a> - 获取所有用户</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/api/users/1">/api/users/:id</a> - 获取单个用户</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/api/users/1/posts">/api/users/:user_id/posts</a> - 获取用户帖子</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/api/users/1/posts/1">/api/users/:user_id/posts/:post_id</a> - 获取特定帖子</div>
<div class="endpoint"><span class="method post">POST</span> /api/users - 创建用户</div>
</div>
<div class="group">
<h2>🔐 Admin API (/admin)</h2>
<div class="endpoint"><span class="method get">GET</span> <a href="/admin">/admin</a> - 管理仪表盘</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/admin/users">/admin/users</a> - 用户管理</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/admin/settings">/admin/settings</a> - 系统设置</div>
</div>
<div class="group">
<h2>🔧 其他端点</h2>
<div class="endpoint"><span class="method get">GET</span> <a href="/health">/health</a> - 健康检查</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/api/version">/api/version</a> - API 版本</div>
</div>
<div class="group">
<h2>💻 代码示例</h2>
<pre>// 创建路由组
mut books := hono.Hono.new()
// 在子应用中定义相对路径
books.get("/", handler) // -> /api/books
books.get("/:id", handler) // -> /api/books/:id
books.post("/", handler) // -> /api/books
// 挂载到主应用
app.route("/api/books", mut books)</pre>
</div>
</body>
</html>'
}
// 打印路由信息
fn print_routes_info() {
println('')
println('📍 服务器地址: http://localhost:8080')
println('')
println('📚 Books API (/api/books):')
println(' GET /api/books - 获取所有书籍')
println(' GET /api/books/:id - 获取单本书籍')
println(' POST /api/books - 创建书籍')
println(' PUT /api/books/:id - 更新书籍')
println(' DELETE /api/books/:id - 删除书籍')
println('')
println('👥 Users API (/api/users):')
println(' GET /api/users - 获取所有用户')
println(' GET /api/users/:id - 获取单个用户')
println(' GET /api/users/:user_id/posts - 获取用户帖子')
println(' GET /api/users/:user_id/posts/:post_id - 获取特定帖子')
println(' POST /api/users - 创建用户')
println('')
println('🔐 Admin API (/admin):')
println(' GET /admin - 管理仪表盘')
println(' GET /admin/users - 用户管理')
println(' GET /admin/settings - 系统设置')
println('')
}
@@ -0,0 +1,280 @@
// V-Hono 路由分组增强示例
// 演示新功能:子应用中间件继承、all() 方法、notFound/onError 处理器
import net.http
import hono
fn main() {
println('🚀 V-Hono 路由分组增强示例启动中...')
// 创建主应用
mut app := hono.Hono.new()
// ========================================
// 1. 自定义 notFound 处理器
// ========================================
app.not_found(fn (mut c hono.Context) http.Response {
c.status(404)
return c.json('{"error": "Not Found", "message": "The requested resource does not exist", "path": "${c.path}"}')
})
// ========================================
// 2. 自定义 onError 处理器
// ========================================
app.on_error(fn (error_msg string, status_code int, mut c hono.Context) http.Response {
c.status(status_code)
return c.json('{"error": "Internal Error", "message": "${error_msg}", "code": ${status_code}}')
})
// ========================================
// 3. 全局中间件
// ========================================
app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
println('[GLOBAL] ${c.req.method} ${c.path}')
return next(mut c)
})
// 根路由
app.get('/', fn (mut c hono.Context) http.Response {
return c.html(generate_index_page())
})
// ========================================
// 4. all() 方法示例 - 匹配所有 HTTP 方法
// ========================================
app.all('/echo', fn (mut c hono.Context) http.Response {
return c.json('{"method": "${c.req.method}", "path": "${c.path}", "message": "Echo endpoint handles all HTTP methods"}')
})
// ========================================
// 5. 子应用中间件继承示例 - API 路由组
// ========================================
mut api := hono.Hono.new()
// API 子应用的中间件(只对 /api/* 路由生效)
api.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
println('[API] Request to API endpoint: ${c.path}')
// 添加 API 版本头
c.headers['X-API-Version'] = '1.0'
return next(mut c)
})
api.get('/version', fn (mut c hono.Context) http.Response {
return c.json('{"version": "1.0.0", "name": "V-Hono API"}')
})
app.route('/api', mut api)
// ========================================
// 6. Books 子应用(带认证中间件)
// ========================================
mut books := hono.Hono.new()
// Books 子应用的认证中间件
books.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
println('[BOOKS] Auth check for: ${c.path}')
// 模拟认证检查
auth_header := c.req.header.get_custom('Authorization') or { '' }
if auth_header == '' {
println('[BOOKS] No auth header, allowing public access')
} else {
println('[BOOKS] Auth header present: ${auth_header}')
}
return next(mut c)
})
books.get('/', fn (mut c hono.Context) http.Response {
return c.json('[{"id": 1, "title": "V Programming"}, {"id": 2, "title": "Web Development"}]')
})
books.get('/:id', fn (mut c hono.Context) http.Response {
book_id := c.params['id']
return c.json('{"id": "${book_id}", "title": "V Programming", "author": "V Team"}')
})
books.post('/', fn (mut c hono.Context) http.Response {
c.status(201)
return c.json('{"message": "Book created", "body": "${c.body}"}')
})
// 使用 all() 处理所有方法
books.all('/stats', fn (mut c hono.Context) http.Response {
return c.json('{"method": "${c.req.method}", "total_books": 100, "message": "Stats endpoint"}')
})
app.route('/api/books', mut books)
// ========================================
// 7. Admin 子应用(带严格认证中间件)
// ========================================
mut admin := hono.Hono.new()
// Admin 子应用的严格认证中间件
admin.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response {
println('[ADMIN] Strict auth check for: ${c.path}')
auth_header := c.req.header.get_custom('Authorization') or { '' }
if !auth_header.starts_with('Bearer admin-') {
c.status(401)
return c.json('{"error": "Unauthorized", "message": "Admin access required"}')
}
println('[ADMIN] Admin access granted')
return next(mut c)
})
admin.get('/', fn (mut c hono.Context) http.Response {
return c.json('{"page": "Admin Dashboard", "stats": {"users": 100, "books": 50}}')
})
admin.get('/users', fn (mut c hono.Context) http.Response {
return c.json('[{"id": 1, "name": "Admin User", "role": "admin"}]')
})
app.route('/admin', mut admin)
// ========================================
// 8. 健康检查(无中间件)
// ========================================
app.get('/health', fn (mut c hono.Context) http.Response {
return c.json('{"status": "ok"}')
})
// 打印路由信息
print_routes_info()
// 启动服务器
app.listen(':8080')
}
// 生成首页 HTML
fn generate_index_page() string {
return '<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>V-Hono 路由分组增强示例</title>
<style>
body { font-family: Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; }
.group { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 8px; }
.endpoint { background: #f5f5f5; padding: 10px; margin: 5px 0; border-radius: 4px; font-family: monospace; }
.method { display: inline-block; width: 70px; font-weight: bold; }
.get { color: #61affe; }
.post { color: #49cc90; }
.all { color: #9b59b6; }
h2 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; }
.feature { background: #e8f4fd; padding: 10px; margin: 10px 0; border-radius: 4px; border-left: 4px solid #007bff; }
pre { background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 5px; overflow-x: auto; }
.middleware { color: #e74c3c; font-size: 0.9em; }
</style>
</head>
<body>
<h1>🚀 V-Hono 路由分组增强示例</h1>
<div class="feature">
<strong>新功能演示:</strong>
<ul>
<li>✅ 子应用中间件继承 - 子应用的中间件只对其路由前缀生效</li>
<li>✅ all() 方法 - 一次性注册所有 HTTP 方法</li>
<li>✅ notFound() - 自定义 404 处理器</li>
<li>✅ onError() - 自定义错误处理器</li>
</ul>
</div>
<div class="group">
<h2>🔧 通用端点</h2>
<div class="endpoint"><span class="method get">GET</span> <a href="/">/</a> - 首页</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/health">/health</a> - 健康检查</div>
<div class="endpoint"><span class="method all">ALL</span> <a href="/echo">/echo</a> - Echo(支持所有 HTTP 方法)</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/not-exist">/not-exist</a> - 测试自定义 404</div>
</div>
<div class="group">
<h2>📡 API 路由组 (/api)</h2>
<p class="middleware">中间件:添加 X-API-Version 头</p>
<div class="endpoint"><span class="method get">GET</span> <a href="/api/version">/api/version</a> - API 版本</div>
</div>
<div class="group">
<h2>📚 Books API (/api/books)</h2>
<p class="middleware">中间件:认证检查(可选)</p>
<div class="endpoint"><span class="method get">GET</span> <a href="/api/books">/api/books</a> - 获取所有书籍</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/api/books/1">/api/books/:id</a> - 获取单本书籍</div>
<div class="endpoint"><span class="method post">POST</span> /api/books - 创建书籍</div>
<div class="endpoint"><span class="method all">ALL</span> <a href="/api/books/stats">/api/books/stats</a> - 统计(支持所有方法)</div>
</div>
<div class="group">
<h2>🔐 Admin API (/admin)</h2>
<p class="middleware">中间件:严格认证(需要 Bearer admin-* token)</p>
<div class="endpoint"><span class="method get">GET</span> <a href="/admin">/admin</a> - 管理仪表盘(需认证)</div>
<div class="endpoint"><span class="method get">GET</span> <a href="/admin/users">/admin/users</a> - 用户管理(需认证)</div>
</div>
<div class="group">
<h2>💻 代码示例</h2>
<pre>// 1. 自定义 notFound 处理器
app.not_found(fn (mut c hono.Context) http.Response {
c.status(404)
return c.json(\'{"error": "Not Found"}\')
})
// 2. all() 方法 - 匹配所有 HTTP 方法
app.all(\'/echo\', fn (mut c hono.Context) http.Response {
return c.json(\'{"method": "\' + c.req.method + \'"}\')
})
// 3. 子应用中间件继承
mut books := hono.Hono.new()
books.use(auth_middleware) // 只对 /api/books/* 生效
books.get(\'/\', handler)
app.route(\'/api/books\', mut books)</pre>
</div>
<div class="group">
<h2>🧪 测试命令</h2>
<pre># 测试 all() 方法
curl http://localhost:8080/echo
curl -X POST http://localhost:8080/echo
curl -X PUT http://localhost:8080/echo
# 测试自定义 404
curl http://localhost:8080/not-exist
# 测试 Admin 认证中间件
curl http://localhost:8080/admin # 返回 401
curl -H "Authorization: Bearer admin-token" http://localhost:8080/admin # 成功</pre>
</div>
</body>
</html>'
}
// 打印路由信息
fn print_routes_info() {
println('')
println('📍 服务器地址: http://localhost:8080')
println('')
println('🆕 新功能:')
println(' - notFound(): 自定义 404 处理器')
println(' - onError(): 自定义错误处理器')
println(' - all(): 匹配所有 HTTP 方法')
println(' - 子应用中间件继承')
println('')
println('🔧 通用端点:')
println(' GET / - 首页')
println(' GET /health - 健康检查')
println(' ALL /echo - Echo(所有方法)')
println('')
println('📡 API (/api):')
println(' GET /api/version - API 版本')
println('')
println('📚 Books API (/api/books):')
println(' GET /api/books - 获取所有书籍')
println(' GET /api/books/:id - 获取单本书籍')
println(' POST /api/books - 创建书籍')
println(' ALL /api/books/stats - 统计')
println('')
println('🔐 Admin API (/admin) - 需要认证:')
println(' GET /admin - 管理仪表盘')
println(' GET /admin/users - 用户管理')
println('')
}
+55
View File
@@ -0,0 +1,55 @@
// v-hono 服务器示例 - 用于性能对比测试
// 运行: v run server_hono.v
// 测试: curl http://localhost:8081/
module main
import hono
import net.http
fn main() {
mut app := hono.Hono.new()
// 静态路由
app.get('/', fn (mut c hono.Context) http.Response {
return c.text('Hello World')
})
app.get('/api/health', fn (mut c hono.Context) http.Response {
return c.text('OK')
})
app.get('/api/users', fn (mut c hono.Context) http.Response {
return c.json('{"users": []}')
})
app.post('/api/users', fn (mut c hono.Context) http.Response {
return c.json('{"created": true}')
})
// 动态路由
app.get('/api/users/:id', fn (mut c hono.Context) http.Response {
id := c.params['id'] or { '' }
return c.json('{"id": "${id}"}')
})
app.get('/api/users/:id/posts', fn (mut c hono.Context) http.Response {
id := c.params['id'] or { '' }
return c.json('{"posts": [], "user_id": "${id}"}')
})
app.get('/api/users/:user_id/posts/:post_id', fn (mut c hono.Context) http.Response {
user_id := c.params['user_id'] or { '' }
post_id := c.params['post_id'] or { '' }
return c.json('{"user_id": "${user_id}", "post_id": "${post_id}"}')
})
app.get('/api/categories/:cat/items/:item', fn (mut c hono.Context) http.Response {
cat := c.params['cat'] or { '' }
item := c.params['item'] or { '' }
return c.json('{"category": "${cat}", "item": "${item}"}')
})
println('启动 v-hono 服务器在端口 8081...')
app.listen(':8081')
}
+58
View File
@@ -0,0 +1,58 @@
// vweb 服务器示例 - 用于性能对比测试
// 运行: v run server_vweb.v
// 测试: curl http://localhost:8080/
module main
import vweb
struct App {
vweb.Context
}
fn main() {
println('启动 vweb 服务器在端口 8080...')
vweb.run(&App{}, 8080)
}
// 静态路由
@['/']
pub fn (mut app App) index() vweb.Result {
return app.text('Hello World')
}
@['/api/health']
pub fn (mut app App) health() vweb.Result {
return app.text('OK')
}
@['/api/users'; get]
pub fn (mut app App) get_users() vweb.Result {
return app.json('{"users": []}')
}
@['/api/users'; post]
pub fn (mut app App) create_user() vweb.Result {
return app.json('{"created": true}')
}
// 动态路由 - vweb 使用 :param 语法
@['/api/users/:id']
pub fn (mut app App) get_user(id string) vweb.Result {
return app.json('{"id": "${id}"}')
}
@['/api/users/:id/posts']
pub fn (mut app App) get_user_posts(id string) vweb.Result {
return app.json('{"posts": [], "user_id": "${id}"}')
}
@['/api/users/:user_id/posts/:post_id']
pub fn (mut app App) get_user_post(user_id string, post_id string) vweb.Result {
return app.json('{"user_id": "${user_id}", "post_id": "${post_id}"}')
}
@['/api/categories/:cat/items/:item']
pub fn (mut app App) get_category_item(cat string, item string) vweb.Result {
return app.json('{"category": "${cat}", "item": "${item}"}')
}
+126
View File
@@ -0,0 +1,126 @@
import hono
import net.http
import os
fn main() {
println('=== 基础文件流式传输测试 ===')
// 直接创建测试文件
test_content := 'Hello World from V-Hono streaming test! '.repeat(1000)
os.write_file('stream_test.txt', test_content) or {
println('❌ 无法创建测试文件: $err')
return
}
defer {
os.rm('stream_test.txt') or { }
}
println('✅ 创建测试文件: stream_test.txt (${test_content.len} bytes)')
// 创建模拟请求
test_req := http.Request{
method: http.Method.get
url: '/test'
data: ''
header: http.new_header()
}
// 测试传统文件服务
println('\n--- 传统文件服务测试 ---')
mut ctx1 := hono.Context.new(test_req, map[string]string{}, map[string]string{}, '')
response1 := ctx1.file('stream_test.txt')
if response1.status_code == 200 && response1.body.len == test_content.len {
println('✅ 传统文件服务: 成功 (${response1.body.len} bytes)')
} else {
println('❌ 传统文件服务: 失败 (状态码: ${response1.status_code}, 大小: ${response1.body.len})')
}
// 测试流式文件服务
println('\n--- 流式文件服务测试 ---')
mut ctx2 := hono.Context.new(test_req, map[string]string{}, map[string]string{}, '')
response2 := ctx2.file_stream('stream_test.txt')
if response2.status_code == 200 && response2.body.len == test_content.len {
println('✅ 流式文件服务: 成功 (${response2.body.len} bytes)')
} else {
println('❌ 流式文件服务: 失败 (状态码: ${response2.status_code}, 大小: ${response2.body.len})')
}
// 测试智能文件服务
println('\n--- 智能文件服务测试 ---')
mut ctx3 := hono.Context.new(test_req, map[string]string{}, map[string]string{}, '')
response3 := ctx3.file_smart('stream_test.txt')
if response3.status_code == 200 && response3.body.len == test_content.len {
println('✅ 智能文件服务: 成功 (${response3.body.len} bytes)')
} else {
println('❌ 智能文件服务: 失败 (状态码: ${response3.status_code}, 大小: ${response3.body.len})')
}
// 测试自定义选项
println('\n--- 自定义选项测试 ---')
custom_options := hono.FileOptions{
stream_threshold: 1024 // 1KB阈值,强制流式传输
buffer_size: 2048
enable_range: true
max_age: 3600
headers: {
'X-Test': 'stream-test'
}
}
mut ctx4 := hono.Context.new(test_req, map[string]string{}, map[string]string{}, '')
response4 := ctx4.file_stream_with_options('stream_test.txt', custom_options)
if response4.status_code == 200 && response4.body.len == test_content.len {
println('✅ 自定义选项: 成功 (${response4.body.len} bytes)')
// 检查自定义头部
if test_header := response4.header.get_custom('X-Test') {
println(' 📋 自定义头部: $test_header')
}
if cache_control := response4.header.get_custom('Cache-Control') {
println(' 🕒 缓存控制: $cache_control')
}
if accept_ranges := response4.header.get_custom('Accept-Ranges') {
println(' 📊 Range支持: $accept_ranges')
}
} else {
println('❌ 自定义选项: 失败 (状态码: ${response4.status_code}, 大小: ${response4.body.len})')
}
// 测试Range请求
println('\n--- Range请求测试 ---')
mut range_header := http.new_header()
range_header.add_custom('Range', 'bytes=0-99') or { }
range_req := http.Request{
method: http.Method.get
url: '/test'
data: ''
header: range_header
}
range_options := hono.FileOptions{
enable_range: true
stream_threshold: 0 // 强制流式传输
}
mut ctx5 := hono.Context.new(range_req, map[string]string{}, map[string]string{}, '')
response5 := ctx5.file_stream_with_options('stream_test.txt', range_options)
if response5.status_code == 206 { // Partial Content
println('✅ Range请求: 成功 (状态码: 206, 内容长度: ${response5.body.len})')
if content_range := response5.header.get_custom('Content-Range') {
println(' 📊 Content-Range: $content_range')
}
} else {
println('❌ Range请求: 失败 (状态码: ${response5.status_code})')
}
println('\n🎉 所有测试完成!')
}
+242
View File
@@ -0,0 +1,242 @@
import hono
import net.http
import os
import time
import strings
fn main() {
println('=== 文件流式传输测试 ===')
// 创建测试文件
create_test_files()
// 创建应用
mut app := hono.Hono.new()
// 测试路由
setup_test_routes(mut app)
// 在后台启动服务器
go app.listen(':8081')
// 等待服务器启动
time.sleep(2 * time.second)
println('服务器已启动在 http://localhost:8081')
// 执行测试
run_tests()
println('\n所有测试完成!')
}
fn create_test_files() {
println('创建测试文件...')
// 创建小文件 (1KB)
small_content := 'Hello World! '.repeat(80) // 约1KB
os.write_file('test_small.txt', small_content) or { panic(err) }
println(' 创建小文件: test_small.txt (${small_content.len} bytes)')
// 创建中等文件 (1MB)
mut medium_content := strings.new_builder(1024 * 1024)
for i in 0 .. 1000 {
medium_content.write_string('This is line ${i:04d} with some content to make it longer.\n')
}
medium_str := medium_content.str()
os.write_file('test_medium.txt', medium_str) or { panic(err) }
println(' 创建中等文件: test_medium.txt (${medium_str.len} bytes)')
// 创建大文件 (模拟5MB)
mut large_content := strings.new_builder(5 * 1024 * 1024)
base_line := 'This is a long line of text that will be repeated many times to create a large file for testing streaming functionality. '
for i in 0 .. 50000 { // 约5MB
large_content.write_string('${i:06d}: $base_line\n')
}
large_str := large_content.str()
os.write_file('test_large.txt', large_str) or { panic(err) }
println(' 创建大文件: test_large.txt (${large_str.len} bytes)')
}
fn setup_test_routes(mut app hono.Hono) {
// 传统文件服务(内存加载)
app.get('/traditional/:filename', fn (mut c hono.Context) http.Response {
filename := c.params['filename']
return c.file(filename)
})
// 流式文件服务
app.get('/stream/:filename', fn (mut c hono.Context) http.Response {
filename := c.params['filename']
return c.file_stream(filename)
})
// 智能文件服务(自动选择)
app.get('/smart/:filename', fn (mut c hono.Context) http.Response {
filename := c.params['filename']
return c.file_smart(filename)
})
// 带自定义选项的流式服务
app.get('/custom/:filename', fn (mut c hono.Context) http.Response {
filename := c.params['filename']
options := hono.FileOptions{
stream_threshold: 100 * 1024 // 100KB阈值
buffer_size: 4096 // 4KB缓冲区
enable_range: true
max_age: 3600
}
return c.file_stream_with_options(filename, options)
})
// Range请求测试
app.get('/range/:filename', fn (mut c hono.Context) http.Response {
filename := c.params['filename']
options := hono.FileOptions{
enable_range: true
stream_threshold: 1024 // 1KB阈值,强制使用流式传输
}
return c.file_stream_with_options(filename, options)
})
}
fn run_tests() {
println('\n开始性能测试...')
// 测试1:小文件性能对比
test_small_file_performance()
// 测试2:中等文件性能对比
test_medium_file_performance()
// 测试3:大文件性能对比
test_large_file_performance()
// 测试4:Range请求测试
test_range_requests()
// 测试5:智能选择测试
test_smart_selection()
}
fn test_small_file_performance() {
println('\n--- 小文件性能测试 ---')
// 传统方式
start := time.now()
for i in 0 .. 100 {
_ = os.execute('curl -s http://localhost:8081/traditional/test_small.txt > nul')
}
traditional_time := time.now() - start
println('传统方式 100次请求: $traditional_time')
// 流式方式
start2 := time.now()
for i in 0 .. 100 {
_ = os.execute('curl -s http://localhost:8081/stream/test_small.txt > nul')
}
stream_time := time.now() - start2
println('流式方式 100次请求: $stream_time')
// 智能方式
start3 := time.now()
for i in 0 .. 100 {
_ = os.execute('curl -s http://localhost:8081/smart/test_small.txt > nul')
}
smart_time := time.now() - start3
println('智能方式 100次请求: $smart_time')
}
fn test_medium_file_performance() {
println('\n--- 中等文件性能测试 ---')
// 传统方式
start := time.now()
for i in 0 .. 10 {
_ = os.execute('curl -s http://localhost:8081/traditional/test_medium.txt > nul')
}
traditional_time := time.now() - start
println('传统方式 10次请求: $traditional_time')
// 流式方式
start2 := time.now()
for i in 0 .. 10 {
_ = os.execute('curl -s http://localhost:8081/stream/test_medium.txt > nul')
}
stream_time := time.now() - start2
println('流式方式 10次请求: $stream_time')
}
fn test_large_file_performance() {
println('\n--- 大文件性能测试 ---')
// 只测试流式方式,避免内存问题
start := time.now()
for i in 0 .. 5 {
result := os.execute('curl -s http://localhost:8081/stream/test_large.txt > nul')
if result.exit_code != 0 {
println(' 请求 $i 失败')
}
}
stream_time := time.now() - start
println('流式方式 5次大文件请求: $stream_time')
// 智能方式(应该自动选择流式传输)
start2 := time.now()
for i in 0 .. 5 {
result := os.execute('curl -s http://localhost:8081/smart/test_large.txt > nul')
if result.exit_code != 0 {
println(' 智能方式请求 $i 失败')
}
}
smart_time := time.now() - start2
println('智能方式 5次大文件请求: $smart_time')
}
fn test_range_requests() {
println('\n--- Range请求测试 ---')
// 测试部分内容请求
result1 := os.execute('curl -s -H "Range: bytes=0-99" http://localhost:8081/range/test_medium.txt')
if result1.exit_code == 0 {
println('✅ Range请求 0-99: 成功')
} else {
println('❌ Range请求 0-99: 失败')
}
result2 := os.execute('curl -s -H "Range: bytes=100-199" http://localhost:8081/range/test_medium.txt')
if result2.exit_code == 0 {
println('✅ Range请求 100-199: 成功')
} else {
println('❌ Range请求 100-199: 失败')
}
}
fn test_smart_selection() {
println('\n--- 智能选择测试 ---')
// 测试小文件(应该使用内存加载)
result1 := os.execute('curl -s http://localhost:8081/smart/test_small.txt')
if result1.exit_code == 0 {
println('✅ 智能选择小文件: 成功')
} else {
println('❌ 智能选择小文件: 失败')
}
// 测试大文件(应该使用流式传输)
result2 := os.execute('curl -s -o large_output.txt http://localhost:8081/smart/test_large.txt')
if result2.exit_code == 0 {
// 检查下载的文件大小
if os.exists('large_output.txt') {
downloaded_size := os.file_size('large_output.txt') or { 0 }
original_size := os.file_size('test_large.txt') or { 0 }
if downloaded_size == original_size {
println('✅ 智能选择大文件: 成功 (${downloaded_size} bytes)')
} else {
println('❌ 智能选择大文件: 大小不匹配 (${downloaded_size} vs ${original_size})')
}
os.rm('large_output.txt') or {}
}
} else {
println('❌ 智能选择大文件: 失败')
}
}