refc: Refactor project structure
This commit is contained in:
@@ -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('✅ 示例用户已创建')
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
@@ -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所有测试完成! 🎉')
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
@@ -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('')
|
||||
}
|
||||
@@ -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')
|
||||
}
|
||||
@@ -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}"}')
|
||||
}
|
||||
@@ -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🎉 所有测试完成!')
|
||||
}
|
||||
@@ -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('❌ 智能选择大文件: 失败')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user