Skip to content
鼓励作者:欢迎打赏犒劳

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)
}

如有转载或 CV 的请标注本站原文地址