module main import net.http import hono fn main() { // ===== 1. 应用初始化 ===== mut app := hono.Hono.new() // ===== 2. 中间件配置 ===== // 日志中间件 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.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { println("url path: ${c.path}") token := c.query['token'] if c.path != "/" && token == '' { c.status(401) return c.json('{"error": "Unauthorized", "message": "Token required"}') } return next(mut c) }) // ===== 3. 静态文件服务中间件 ===== // 默认静态文件服务(./public 目录) app.use(hono.serve_static_default()) // 资源文件服务(./static 目录) app.use(hono.serve_static_path('/assets', './static')) // 上传文件服务(./uploads 目录) options := hono.StaticOptions{ root: './uploads' path: '/files' index: 'index.html' dotfiles: false etag: true last_modified: true max_age: 3600 // 1小时缓存 headers: { 'X-Served-By': 'V-Hono' 'X-Static-Files': 'true' } } app.use(hono.serve_static(options)) // ===== 4. 基础路由 ===== // 根路径 - 欢迎页面 app.get('/', fn (mut c hono.Context) http.Response { html_content := ' V-Hono 完整示例

🚀 V-Hono 完整示例

A lightweight web framework for V with static file serving

📁 静态文件服务

默认目录: / → ./public
资源目录: /assets → ./static
文件目录: /files → ./uploads

测试文件:

🔧 API 端点

健康检查: /api/health
静态文件状态: /api/status
静态文件信息: /api/static-info
基础路由: /hello

📄 文件服务端点

基本文件服务: /file/test.txt
缓存文件服务: /file-cached/test.txt
文件下载: /download/downloadable.txt
图片文件服务: /image/style.css
安全文件服务: /safe-file/test.txt

🎯 动态路由示例

单参数: /users/123
多参数: /posts/456/comments/789
嵌套参数: /api/users/101/posts/202

✨ 特性

🧪 测试命令

# 静态文件测试
curl http://localhost:8080/test.txt
curl http://localhost:8080/assets/style.css
curl http://localhost:8080/files/sample.json

# 文件服务测试
curl http://localhost:8080/file/test.txt
curl http://localhost:8080/file-cached/test.txt
curl http://localhost:8080/download/downloadable.txt
curl http://localhost:8080/image/style.css
curl http://localhost:8080/safe-file/test.txt

# API 测试
curl http://localhost:8080/api/health
curl http://localhost:8080/api/status
curl http://localhost:8080/hello

# 动态路由测试
curl http://localhost:8080/users/123
curl http://localhost:8080/posts/456/comments/789

# POST 测试
curl -X POST http://localhost:8080/api/users -d "{\\"name\\":\\"John\\"}"
curl -X POST "http://localhost:8080/api/test/create/456?token=abc123" -d "{\\"name\\":\\"Test\\"}"
        
' return c.html(html_content) }) // 基础路由 app.get('/hello', fn (mut c hono.Context) http.Response { return c.text('Hello, World!') }) // Path 信息展示路由 app.get('/path-info', fn (mut c hono.Context) http.Response { // 构建JSON响应 mut response := '{' response += '"path_info": {' response += '"path": "${c.path}",' response += '"url": "${c.url}"' response += '}' response += '}' return c.json(response) }) // ===== 5. API 路由 ===== // 健康检查 app.get('/api/health', fn (mut c hono.Context) http.Response { c.status(200) return c.json('{"status": "ok", "message": "Health check passed"}') }) // 静态文件服务状态 app.get('/api/status', fn (mut c hono.Context) http.Response { return c.json('{"status": "ok", "static_files": "enabled"}') }) // 静态文件服务信息 app.get('/api/static-info', fn (mut c hono.Context) http.Response { return c.json('{ "static_directories": [ {"path": "/", "root": "./public"}, {"path": "/assets", "root": "./static"}, {"path": "/files", "root": "./uploads"} ], "features": [ "Path traversal protection", "Auto index files", "ETag support", "Last-Modified headers", "Configurable caching", "Custom headers" ] }') }) // ===== 6. 动态路由 ===== // 单参数路由 app.get('/users/:id', fn (mut c hono.Context) http.Response { user_id := c.params['id'] return c.json('{"user_id": "${user_id}", "name": "John Doe"}') }) // 多参数路由 app.get('/posts/:id/comments/:comment_id', fn (mut c hono.Context) http.Response { post_id := c.params['id'] comment_id := c.params['comment_id'] return c.json('{"post_id": "${post_id}", "comment_id": "${comment_id}", "content": "Great post!"}') }) // 嵌套参数路由 app.get('/api/users/:user_id/posts/:post_id', fn (mut c hono.Context) http.Response { user_id := c.params['user_id'] post_id := c.params['post_id'] return c.json('{"user_id": "${user_id}", "post_id": "${post_id}", "title": "Sample Post"}') }) // 路径分析路由 app.get('/analyze/*path', fn (mut c hono.Context) http.Response { requested_path := c.params['path'] // 构建JSON响应 mut response := '{' response += '"path_analysis": {' response += '"original_path": "${c.path}",' response += '"requested_path": "${requested_path}"' response += '}' response += '}' return c.json(response) }) // ===== 7. HTTP 方法示例 ===== // POST 请求 app.post('/api/users', fn (mut c hono.Context) http.Response { println('=== POST 请求信息 ===') println('请求体: ${c.body}') println('查询参数: ${c.query}') c.status(201) return c.json('{"message": "User created", "data": "${c.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 mut response := '{' response += '"message": "复杂测试",' response += '"params": {' response += '"action": "${action}",' response += '"id": "${id}"' response += '},' response += '"query": {' // 添加查询参数 mut query_count := 0 for key, value in c.query { if query_count > 0 { response += ',' } response += '"${key}": "${value}"' query_count++ } response += '},' response += '"body": "${c.body}"' response += '}' c.status(200) return c.json(response) }) // PUT 请求示例 app.put('/api/users/:id', fn (mut c hono.Context) http.Response { user_id := c.params['id'] c.status(200) return c.json('{"message": "User replaced", "user_id": "${user_id}", "data": "${c.body}"}') }) // DELETE 请求示例 app.delete('/api/users/:id', fn (mut c hono.Context) http.Response { _ := c.params['id'] // 忽略用户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}"}') }) // HEAD 请求示例 app.head('/api/users/:id', fn (mut c hono.Context) http.Response { user_id := c.params['id'] c.headers['X-User-ID'] = user_id c.status(200) return c.text('') }) // OPTIONS 请求示例 app.options('/api/users/:id', fn (mut c hono.Context) http.Response { c.headers['Allow'] = 'GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS' c.headers['Access-Control-Allow-Origin'] = '*' c.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS' c.status(200) return c.text('') }) // ===== 8. 文件服务功能 ===== // 基本文件服务示例 app.get('/file/:filename', fn (mut c hono.Context) http.Response { filename := c.params['filename'] file_path := 'public/${filename}' return c.file(file_path) }) // 带选项的文件服务示例 app.get('/file-cached/:filename', fn (mut c hono.Context) http.Response { filename := c.params['filename'] file_path := 'public/${filename}' options := hono.FileOptions{ max_age: 3600 // 缓存1小时 headers: { 'X-Served-By': 'v-hono' } } return c.file_with_options(file_path, options) }) // 自定义Content-Type的文件服务 app.get('/download/:filename', fn (mut c hono.Context) http.Response { filename := c.params['filename'] file_path := 'uploads/${filename}' options := hono.FileOptions{ content_type: 'application/octet-stream' headers: { 'Content-Disposition': 'attachment; filename="${filename}"' } } return c.file_with_options(file_path, options) }) // 图片文件服务示例 app.get('/image/:filename', fn (mut c hono.Context) http.Response { filename := c.params['filename'] file_path := 'static/${filename}' options := hono.FileOptions{ max_age: 86400 // 缓存24小时 headers: { 'X-Image-Type': 'static' } } return c.file_with_options(file_path, options) }) // 禁用缓存的文件服务 app.get('/dynamic/:filename', fn (mut c hono.Context) http.Response { filename := c.params['filename'] file_path := 'dynamic/${filename}' options := hono.FileOptions{ no_cache: true headers: { 'X-Dynamic': 'true' } } return c.file_with_options(file_path, options) }) // ===== 9. 错误处理示例 ===== // 安全文件服务 app.get('/safe-file/:filename', fn (mut c hono.Context) http.Response { filename := c.params['filename'] // 安全检查:只允许访问特定目录 if filename.contains('..') || filename.starts_with('/') { c.status(403) return c.text('Access denied') } file_path := 'public/${filename}' return c.file(file_path) }) // 404 错误处理 app.get('/**', fn (mut c hono.Context) http.Response { c.status(404) return c.json('{"error": "Not Found", "message": "The requested resource was not found"}') }) // ===== 10. 服务器启动 ===== println('🚀 V-Hono 完整示例启动中...') println('📍 服务器地址: http://localhost:8080') println('') println('📁 静态文件服务:') println(' - 默认: ./public (访问路径: /)') println(' - 资源: ./static (访问路径: /assets)') println(' - 文件: ./uploads (访问路径: /files)') println('') println('🔧 API 端点:') println(' - GET /api/health') println(' - GET /api/status') println(' - GET /api/static-info') println(' - GET /hello') println(' - GET /path-info') println(' - GET /analyze/*path') println('') println('📄 文件服务端点:') println(' - GET /file/:filename - 基本文件服务') println(' - GET /file-cached/:filename - 缓存文件服务') println(' - GET /download/:filename - 文件下载') println(' - GET /image/:filename - 图片文件服务') println(' - GET /dynamic/:filename - 动态文件服务') println(' - GET /safe-file/:filename - 安全文件服务') println('') println('🎯 动态路由:') println(' - GET /users/123') println(' - GET /posts/456/comments/789') println(' - GET /api/users/101/posts/202') println('') println('📝 HTTP 方法示例:') println(' - POST /api/users') println(' - POST /api/users/123') println(' - POST /api/test/create/456') println(' - PUT /api/users/123') println(' - DELETE /api/users/123') println(' - PATCH /api/users/123') println(' - HEAD /api/users/123') println(' - OPTIONS /api/users/123') println('') println('🧪 测试命令示例:') println(' # 静态文件测试') println(' curl http://localhost:8080/test.txt') println(' curl http://localhost:8080/assets/style.css') println(' curl http://localhost:8080/files/sample.json') println('') println(' # 文件服务测试') println(' curl http://localhost:8080/file/test.txt') println(' curl http://localhost:8080/file-cached/test.txt') println(' curl http://localhost:8080/download/downloadable.txt') println(' curl http://localhost:8080/image/style.css') println(' curl http://localhost:8080/safe-file/test.txt') println('') println(' # API 测试') println(' curl http://localhost:8080/api/health') println(' curl http://localhost:8080/api/status') println(' curl http://localhost:8080/hello') println('') println(' # 动态路由测试') println(' curl http://localhost:8080/users/123') println(' curl http://localhost:8080/posts/456/comments/789') println('') println(' # POST 测试') 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\\"}"') app.listen(':8080') }