自定义中间件 #
一、中间件基础结构 #
1.1 基本结构 #
go
func MyMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
return next(c)
}
}
1.2 使用中间件 #
go
e := echo.New()
e.Use(MyMiddleware)
二、简单中间件示例 #
2.1 日志中间件 #
go
func loggingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
err := next(c)
duration := time.Since(start)
method := c.Request().Method
path := c.Request().URL.Path
status := c.Response().Status
log.Printf("[%s] %s %d %v", method, path, status, duration)
return err
}
}
2.2 计时中间件 #
go
func timingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
err := next(c)
duration := time.Since(start)
c.Response().Header().Set("X-Response-Time", duration.String())
return err
}
}
2.3 请求ID中间件 #
go
func requestIDMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
requestID := c.Request().Header.Get("X-Request-ID")
if requestID == "" {
requestID = uuid.New().String()
}
c.Response().Header().Set("X-Request-ID", requestID)
c.Set("requestID", requestID)
return next(c)
}
}
三、认证中间件 #
3.1 API Key认证 #
go
func apiKeyMiddleware(apiKey string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
key := c.Request().Header.Get("X-API-Key")
if key == "" {
return echo.NewHTTPError(http.StatusUnauthorized, "缺少API Key")
}
if key != apiKey {
return echo.NewHTTPError(http.StatusUnauthorized, "API Key无效")
}
return next(c)
}
}
}
e.Use(apiKeyMiddleware("your-secret-key"))
3.2 Token认证 #
go
func tokenMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
auth := c.Request().Header.Get("Authorization")
if auth == "" {
return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
}
parts := strings.Split(auth, " ")
if len(parts) != 2 || parts[0] != "Bearer" {
return echo.NewHTTPError(http.StatusUnauthorized, "Token格式错误")
}
token := parts[1]
user, err := validateToken(token)
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "Token无效")
}
c.Set("user", user)
return next(c)
}
}
3.3 权限验证中间件 #
go
func roleMiddleware(roles ...string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
user := c.Get("user").(*User)
for _, role := range roles {
if user.Role == role {
return next(c)
}
}
return echo.NewHTTPError(http.StatusForbidden, "权限不足")
}
}
}
admin := e.Group("/admin")
admin.Use(roleMiddleware("admin"))
四、请求处理中间件 #
4.1 请求体大小限制 #
go
func bodyLimitMiddleware(limit int64) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
contentLength := c.Request().ContentLength
if contentLength > limit {
return echo.NewHTTPError(http.StatusRequestEntityTooLarge, "请求体过大")
}
return next(c)
}
}
}
4.2 请求体读取缓存 #
go
func bodyDumpMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
body, err := io.ReadAll(c.Request().Body)
if err != nil {
return err
}
c.Request().Body = io.NopCloser(bytes.NewBuffer(body))
c.Set("requestBody", string(body))
return next(c)
}
}
4.3 请求签名验证 #
go
func signatureMiddleware(secret string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
signature := c.Request().Header.Get("X-Signature")
timestamp := c.Request().Header.Get("X-Timestamp")
if signature == "" || timestamp == "" {
return echo.NewHTTPError(http.StatusUnauthorized, "缺少签名")
}
body, _ := io.ReadAll(c.Request().Body)
c.Request().Body = io.NopCloser(bytes.NewBuffer(body))
expected := hmacSHA256(secret, timestamp+string(body))
if signature != expected {
return echo.NewHTTPError(http.StatusUnauthorized, "签名验证失败")
}
return next(c)
}
}
}
五、响应处理中间件 #
5.1 响应包装 #
go
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
func responseWrapperMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
err := next(c)
if err != nil {
return err
}
return nil
}
}
5.2 响应头添加 #
go
func headersMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Response().Header().Set("X-Server", "Echo")
c.Response().Header().Set("X-Version", "1.0.0")
return next(c)
}
}
5.3 响应日志 #
go
func responseLogMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
rw := &responseWriter{ResponseWriter: c.Response().Writer}
c.Response().Writer = rw
err := next(c)
log.Printf("Response: %s", rw.body)
return err
}
}
type responseWriter struct {
http.ResponseWriter
body []byte
}
func (w *responseWriter) Write(b []byte) (int, error) {
w.body = append(w.body, b...)
return w.ResponseWriter.Write(b)
}
六、可配置中间件 #
6.1 配置结构 #
go
type AuthConfig struct {
Skipper middleware.Skipper
TokenLookup string
AuthScheme string
Key string
}
func AuthWithConfig(config AuthConfig) echo.MiddlewareFunc {
if config.Skipper == nil {
config.Skipper = middleware.DefaultSkipper
}
if config.TokenLookup == "" {
config.TokenLookup = "header:Authorization:Bearer "
}
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if config.Skipper(c) {
return next(c)
}
token := extractToken(c, config.TokenLookup)
if token == "" {
return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
}
user, err := validateToken(token, config.Key)
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, "Token无效")
}
c.Set("user", user)
return next(c)
}
}
}
6.2 使用配置 #
go
e.Use(AuthWithConfig(AuthConfig{
Skipper: func(c echo.Context) bool {
return c.Path() == "/login" || c.Path() == "/register"
},
TokenLookup: "header:Authorization:Bearer ",
Key: "secret-key",
}))
七、中间件组合 #
7.1 链式组合 #
go
func chain(middlewares ...echo.MiddlewareFunc) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
for i := len(middlewares) - 1; i >= 0; i-- {
next = middlewares[i](next)
}
return next
}
}
e.Use(chain(
loggingMiddleware,
timingMiddleware,
authMiddleware,
))
7.2 条件组合 #
go
func conditionalMiddleware(condition func(c echo.Context) bool, middleware echo.MiddlewareFunc) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
wrapped := middleware(next)
return func(c echo.Context) error {
if condition(c) {
return wrapped(c)
}
return next(c)
}
}
}
e.Use(conditionalMiddleware(
func(c echo.Context) bool {
return strings.HasPrefix(c.Path(), "/api")
},
authMiddleware,
))
八、中间件最佳实践 #
8.1 错误处理 #
go
func errorHandlingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
err := next(c)
if err != nil {
if he, ok := err.(*echo.HTTPError); ok {
return c.JSON(he.Code, map[string]interface{}{
"code": he.Code,
"message": he.Message,
})
}
return c.JSON(http.StatusInternalServerError, map[string]interface{}{
"code": 500,
"message": "服务器内部错误",
})
}
return nil
}
}
8.2 Panic恢复 #
go
func panicRecoverMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
log.Printf("Panic recovered: %v\n%s", r, debug.Stack())
}
}()
return next(c)
}
}
8.3 上下文传递 #
go
func contextMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
ctx := context.WithValue(c.Request().Context(), "requestID", c.Get("requestID"))
c.SetRequest(c.Request().WithContext(ctx))
return next(c)
}
}
九、完整示例 #
go
package main
import (
"context"
"log"
"net/http"
"time"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
)
type User struct {
ID string `json:"id"`
Name string `json:"name"`
Role string `json:"role"`
}
func main() {
e := echo.New()
e.Use(requestIDMiddleware)
e.Use(loggingMiddleware)
e.Use(timingMiddleware)
e.Use(errorHandlingMiddleware)
e.GET("/health", health)
api := e.Group("/api")
api.Use(authMiddleware)
api.GET("/users", getUsers)
api.GET("/profile", getProfile)
admin := api.Group("/admin")
admin.Use(roleMiddleware("admin"))
admin.GET("/stats", getStats)
e.Logger.Fatal(e.Start(":8080"))
}
func requestIDMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
requestID := uuid.New().String()
c.Response().Header().Set("X-Request-ID", requestID)
c.Set("requestID", requestID)
return next(c)
}
}
func loggingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
err := next(c)
log.Printf("[%s] %s %s %d %v",
c.Get("requestID"),
c.Request().Method,
c.Request().URL.Path,
c.Response().Status,
time.Since(start),
)
return err
}
}
func timingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
err := next(c)
c.Response().Header().Set("X-Response-Time", time.Since(start).String())
return err
}
}
func errorHandlingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
err := next(c)
if err != nil {
if he, ok := err.(*echo.HTTPError); ok {
return c.JSON(he.Code, map[string]interface{}{
"code": he.Code,
"message": he.Message,
})
}
return c.JSON(http.StatusInternalServerError, map[string]interface{}{
"code": 500,
"message": "服务器内部错误",
})
}
return nil
}
}
func authMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
token := c.Request().Header.Get("Authorization")
if token == "" {
return echo.NewHTTPError(http.StatusUnauthorized, "请登录")
}
user := &User{
ID: "1",
Name: "张三",
Role: "admin",
}
c.Set("user", user)
return next(c)
}
}
func roleMiddleware(role string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
user := c.Get("user").(*User)
if user.Role != role {
return echo.NewHTTPError(http.StatusForbidden, "权限不足")
}
return next(c)
}
}
}
func health(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{
"status": "healthy",
})
}
func getUsers(c echo.Context) error {
return c.JSON(http.StatusOK, []map[string]string{
{"id": "1", "name": "张三"},
{"id": "2", "name": "李四"},
})
}
func getProfile(c echo.Context) error {
user := c.Get("user").(*User)
return c.JSON(http.StatusOK, user)
}
func getStats(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]int{
"users": 100,
})
}
十、总结 #
自定义中间件要点:
| 要点 | 说明 |
|---|---|
| 函数签名 | func(HandlerFunc) HandlerFunc |
| 前置处理 | next(c) 之前执行 |
| 后置处理 | next(c) 之后执行 |
| 数据传递 | c.Set() / c.Get() |
| 错误处理 | 返回 echo.HTTPError |
| 可配置 | 使用配置结构体 |
准备好学习中间件执行顺序了吗?让我们进入下一章!
最后更新:2026-03-28