diff --git a/README.MD b/README.MD index c84003b..7c3929e 100644 --- a/README.MD +++ b/README.MD @@ -56,7 +56,7 @@ fn main() { // 中间件 app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { - println('[LOG] ${c.url}') + println('[LOG] ${c.path}') return next(mut c) }) @@ -205,7 +205,7 @@ return c.json('{"error": "Not Found"}') ```v // 日志中间件 app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { - println('[${time.now()}] ${c.req.method} ${c.url}') + println('[${time.now()}] ${c.req.method} ${c.path}') return next(mut c) }) diff --git a/example.v b/example.v index 17ad776..0c31584 100644 --- a/example.v +++ b/example.v @@ -11,15 +11,15 @@ fn main() { // 日志中间件 app.use(fn (mut c hono.Context, next fn (mut hono.Context) http.Response) http.Response { - println('[LOG] ${c.req.method} ${c.url}') + 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.url}") + println("url path: ${c.path}") token := c.query['token'] - if c.url != "/" && token == '' { + if c.path != "/" && token == '' { c.status(401) return c.json('{"error": "Unauthorized", "message": "Token required"}') } @@ -197,6 +197,19 @@ curl -X POST "http://localhost:8080/api/test/create/456?token=abc123" -d "{\\"na 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 路由 ===== // 健康检查 @@ -251,6 +264,21 @@ curl -X POST "http://localhost:8080/api/test/create/456?token=abc123" -d "{\\"na 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 请求 @@ -323,7 +351,7 @@ curl -X POST "http://localhost:8080/api/test/create/456?token=abc123" -d "{\\"na // DELETE 请求示例 app.delete('/api/users/:id', fn (mut c hono.Context) http.Response { - user_id := c.params['id'] + _ := c.params['id'] // 忽略用户ID c.status(204) return c.text('') }) @@ -458,6 +486,8 @@ curl -X POST "http://localhost:8080/api/test/create/456?token=abc123" -d "{\\"na 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 - 基本文件服务') diff --git a/hono/request.v b/hono/request.v index 43644c9..96a7635 100644 --- a/hono/request.v +++ b/hono/request.v @@ -11,6 +11,7 @@ pub: params map[string]string query map[string]string url string + path string // 当前请求的路径 pub mut: status_code int = 200 headers map[string]string @@ -30,7 +31,8 @@ pub fn Context.new(req http.Request, params map[string]string, query map[string] params: params query: query body: body - url: url.path + url: url.str() // 设置为完整的URL字符串 + path: url.path // 设置 path 属性 headers: map[string]string{} } } @@ -145,7 +147,16 @@ pub fn (mut c Context) file_with_options(file_path string, options FileOptions) if_none_match := c.req.header.get_custom('If-None-Match') or { '' } if if_none_match == etag { c.status(304) - return c.text('') + // 构建响应头 + mut headers := http.new_header() + for key, value in c.headers { + headers.add_custom(key, value) or { continue } + } + return http.Response{ + status_code: c.status_code + header: headers + body: '' + } } } @@ -162,7 +173,15 @@ pub fn (mut c Context) file_with_options(file_path string, options FileOptions) } // 返回文件内容 - return c.text(file_content) + mut headers := http.new_header() + for key, value in c.headers { + headers.add_custom(key, value) or { continue } + } + return http.Response{ + status_code: c.status_code + header: headers + body: file_content + } } // FileOptions struct for configuring file serving @@ -240,6 +259,8 @@ pub fn (mut c Context) status(code int) { c.status_code = code } + + // 处理器接口,使用 Context pub interface IHandler { path string diff --git a/hono/static.v b/hono/static.v index 34cf623..726501f 100644 --- a/hono/static.v +++ b/hono/static.v @@ -35,12 +35,12 @@ pub fn default_static_options() StaticOptions { pub fn serve_static(options StaticOptions) fn (mut Context, fn (mut Context) http.Response) http.Response { return fn [options] (mut c Context, next fn (mut Context) http.Response) http.Response { // 检查请求路径是否匹配静态文件路径 - if !c.url.starts_with(options.path) { + if !c.path.starts_with(options.path) { return next(mut c) } // 提取文件路径 - mut file_path := c.url[options.path.len..] + mut file_path := c.path[options.path.len..] if file_path.starts_with('/') { file_path = file_path[1..] } @@ -50,7 +50,7 @@ pub fn serve_static(options StaticOptions) fn (mut Context, fn (mut Context) htt // 调试信息 println('[DEBUG] Static file request:') - println(' URL: ${c.url}') + println(' Path: ${c.path}') println(' Path prefix: ${options.path}') println(' File path: ${file_path}') println(' Root: ${options.root}') @@ -137,7 +137,16 @@ fn serve_file(mut c Context, file_path string, options StaticOptions) http.Respo if_none_match := c.req.header.get_custom('If-None-Match') or { '' } if if_none_match == etag { c.status(304) - return c.text('') + // 构建响应头 + mut headers := http.new_header() + for key, value in c.headers { + headers.add_custom(key, value) or { continue } + } + return http.Response{ + status_code: c.status_code + header: headers + body: '' + } } } @@ -154,7 +163,15 @@ fn serve_file(mut c Context, file_path string, options StaticOptions) http.Respo } // 返回文件内容 - return c.text(file_content) + mut headers := http.new_header() + for key, value in c.headers { + headers.add_custom(key, value) or { continue } + } + return http.Response{ + status_code: c.status_code + header: headers + body: file_content + } } // 安全检查:防止路径遍历攻击 diff --git a/public/img1.jpg b/public/img1.jpg new file mode 100644 index 0000000..9ec2634 Binary files /dev/null and b/public/img1.jpg differ diff --git a/public/index.html b/public/index.html index 93930b8..9f19dee 100644 --- a/public/index.html +++ b/public/index.html @@ -327,7 +327,7 @@ - + @@ -612,11 +612,64 @@ app.listen(':8080') } const response = await fetch(url.toString()); - const data = await response.text(); const responseTime = Date.now() - startTime; - resultDiv.className = 'result'; - resultDiv.textContent = `✅ 成功 (${responseTime}ms)\n状态码: ${response.status}\n请求URL: ${url.toString()}\n文件大小: ${data.length} 字符\n内容预览:\n${data.substring(0, 200)}${data.length > 200 ? '...' : ''}`; + // 获取所有响应头信息用于调试 + const headers = {}; + response.headers.forEach((value, key) => { + headers[key] = value; + }); + + // 检查是否是图片文件 - 优先检查文件扩展名 + const urlPath = url.pathname.toLowerCase(); + const isImageByExtension = urlPath.endsWith('.jpg') || urlPath.endsWith('.jpeg') || + urlPath.endsWith('.png') || urlPath.endsWith('.gif') || + urlPath.endsWith('.webp') || urlPath.endsWith('.svg'); + + // 也检查 content-type + const contentType = response.headers.get('content-type') || ''; + const isImageByType = contentType.startsWith('image/'); + + const isImage = isImageByExtension || isImageByType; + + if (isImage) { + // 对于图片文件,显示图片预览 + const blob = await response.blob(); + const imageUrl = URL.createObjectURL(blob); + + resultDiv.className = 'result'; + resultDiv.innerHTML = ` + ✅ 成功 (${responseTime}ms)
+ 状态码: ${response.status}
+ 请求URL: ${url.toString()}
+ 文件类型: ${contentType}
+ 文件大小: ${(blob.size / 1024).toFixed(2)} KB
+ 检测方式: ${isImageByExtension ? '文件扩展名' : 'Content-Type'}
+
+ 响应头信息:
+
${JSON.stringify(headers, null, 2)}
+
+ 图片预览:
+ 图片预览 + `; + } else { + // 对于非图片文件,显示文本内容 + const data = await response.text(); + resultDiv.className = 'result'; + resultDiv.innerHTML = ` + ✅ 成功 (${responseTime}ms)
+ 状态码: ${response.status}
+ 请求URL: ${url.toString()}
+ 文件类型: ${contentType}
+ 文件大小: ${data.length} 字符
+
+ 响应头信息:
+
${JSON.stringify(headers, null, 2)}
+
+ 内容预览:
+
${data.substring(0, 200)}${data.length > 200 ? '...' : ''}
+ `; + } updateStats(true, responseTime); } catch (error) { diff --git a/static/img1.jpg b/static/img1.jpg new file mode 100644 index 0000000..9ec2634 Binary files /dev/null and b/static/img1.jpg differ