
hertz中间件
中间件是 Hertz 中非常重要的概念,允许你在请求处理前后执行代码
日志中间件
go
package main
import (
"context"
"fmt"
"time"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/protocol/consts"
)
// 简单的日志中间件
func LoggerMiddleware() app.HandlerFunc {
return func(c context.Context, ctx *app.RequestContext) {
// 记录请求开始时间
start := time.Now()
// 处理请求
ctx.Next(c)
// 记录请求完成时间
latency := time.Since(start)
// 打印日志
fmt.Printf("Method: %s | Path: %s | Status: %d | Latency: %v\n",
ctx.Request.Method(),
ctx.Request.URI().Path(),
ctx.Response.StatusCode(),
latency,
)
}
}
func main() {
h := server.Default()
// 全局使用中间件
h.Use(LoggerMiddleware())
h.GET("/hello", func(c context.Context, ctx *app.RequestContext) {
ctx.String(consts.StatusOK, "Hello, World!")
})
h.Spin()
}认证中间件
go
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/protocol/consts"
)
// 简单的认证中间件
func AuthMiddleware() app.HandlerFunc {
return func(c context.Context, ctx *app.RequestContext) {
token := string(ctx.GetHeader("Authorization"))
if token != "Bearer secret-token" {
ctx.JSON(consts.StatusUnauthorized, map[string]interface{}{
"error": "Invalid or missing token",
})
// 中止请求处理
ctx.Abort()
return
}
// 继续处理请求
ctx.Next(c)
}
}
func main() {
h := server.New()
// 在特定路由上使用中间件
authorized := h.Group("/api", AuthMiddleware())
{
authorized.GET("/data", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(consts.StatusOK, map[string]interface{}{
"data": "认证后的数据",
})
})
}
// 不需要认证的路由
h.GET("/public", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(consts.StatusOK, map[string]interface{}{
"data": "公共数据",
})
})
h.Spin()
}打印入参中间件
go
package utils
import (
"encoding/json"
"fmt"
"net/url"
"strings"
"github.com/cloudwego/hertz/pkg/app"
)
// RequestDump 用于结构化输出所有请求参数
type RequestDump struct {
Query map[string][]string `json:"query,omitempty"`
Path map[string]string `json:"path,omitempty"`
Form map[string][]string `json:"form,omitempty"`
JSON map[string]interface{} `json:"json,omitempty"`
Header map[string][]string `json:"header,omitempty"` // 可选
}
// DumpRequestAll 打印 Hertz 请求中的所有参数(调试专用)
func DumpRequestAll(c *app.RequestContext) RequestDump {
var dump RequestDump
// 初始化字段
dump.Query = make(map[string][]string)
dump.Path = make(map[string]string)
dump.Form = make(map[string][]string)
dump.Header = make(map[string][]string)
fmt.Println("=== HERTZ 请求参数完整打印 ===")
contentType := string(c.Request.Header.ContentType())
// 1. ✅ Query 参数(GET 参数)
c.Request.URI().QueryArgs().VisitAll(func(key, value []byte) {
k := string(key)
dump.Query[k] = append(dump.Query[k], string(value))
})
if len(dump.Query) > 0 {
printMapOfSlice(contentType, "Query Params", dump.Query)
}
// 2. ✅ Path 参数(如 /user/:userId)
for _, p := range c.Params {
dump.Path[p.Key] = p.Value
}
if len(dump.Path) > 0 {
printMapOfSlice(contentType, "Path Params", dump.Path)
}
// 3. ✅ Header(可选打印)
c.Request.Header.VisitAll(func(key, value []byte) {
k := string(key)
dump.Header[k] = append(dump.Header[k], string(value))
})
// 注释掉下面这行如果你不想频繁打印 header
// if len(dump.Header) > 0 {
// fmt.Println("Headers:")
// printMapOfSlice(dump.Header, " ")
// }
// 4. ✅ Body: JSON 或 Form
body := c.Request.Body()
if len(body) > 0 {
// 解析
if len(body) > 1024*1024 { // 超过 1MB
fmt.Println("Body too large, skip parsing")
} else {
// 如果是 JSON
if strings.Contains(contentType, "application/json") {
var jsonData map[string]interface{}
if err := json.Unmarshal(body, &jsonData); err == nil {
dump.JSON = jsonData
printMapOfSlice(contentType, "json", dump.JSON)
} else {
fmt.Printf("JSON 解析失败: %v\n", err)
}
} else if strings.Contains(contentType, "x-www-form-urlencoded") { // 如果是 Form
// ✅ 手动解析 form body
formData, err := url.ParseQuery(string(body))
if err == nil {
// 转成 map[string][]string
for k, v := range formData {
dump.Form[k] = v
}
if len(dump.Form) > 0 {
printMapOfSlice(contentType, "form", dump.Form)
}
} else {
fmt.Printf("Form 解析失败: %v\n", err)
}
} else if strings.Contains(contentType, "multipart/form-data") {
// 解析 multipart 表单
form, err := c.Request.MultipartForm()
if err != nil {
fmt.Printf("Failed to parse multipart form: %v\n", err)
} else {
// 遍历所有文本字段(非文件)
for key, values := range form.Value {
// values 是 []string,因为同一个 key 可能出现多次
dump.Form[key] = values
}
printMapOfSlice(contentType, "multipart-form", dump.Form)
}
}
}
}
fmt.Println("===参数打印结束 ===")
return dump
}
// 工具函数:格式化打印 map[string][]string
func printMapOfSlice(contentType string, source string, m any) {
marshal, _ := json.Marshal(m)
fmt.Printf("contentType : %s , source = %s , request param : %s\n", contentType, source, marshal)
}
