Golang原生http实现中间件

Golang原生http实现中间件

中间件(middleware):常被用来做认证校验、审计等

  • 大家常用的Iris、Gin等web框架,都包含了中间件逻辑。但有时我们引入该框架显得较为繁重,本文将介绍通过golang原生http来实现中间件操作。
  • 全部代码:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/middleware

1 定义http.Handler:具体中间件操作

①CORSMiddleware:允许跨域

go 复制代码
// CORSMiddleware handles Cross-Origin Resource Sharing (CORS) responses.
func CORSMiddleware(next http.Handler) http.Handler {
	fmt.Println("cors middleware....")
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "OPTIONS" {
			w.Header().Set("Access-Control-Allow-Origin", "*")
			w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, DELETE")
			//如果前后端需要传递自定义请求头,需要再Access-Control-Allow-Headers中匹配(Yi-Auth-Token)
			w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, Yi-Auth-Token")
			w.WriteHeader(http.StatusOK)
			return
		}
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Accept,Yi-Auth-Token")
		//交给下一个中间件处理
		next.ServeHTTP(w, r)
	})
}

②AuthMiddleware:认证

go 复制代码
// AuthMiddleware simulates a simple authentication middleware.
func AuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("auth middleware...")
		//store info in ctx
		token := r.Header.Get("Token")
		if len(token) != 0 {
			//TODO 1. check token 2. get userinfo from token
			userID := "1"
			ctx := context.WithValue(r.Context(), "userID", userID)
			r = r.WithContext(ctx)
		}
		next.ServeHTTP(w, r)
	})
}

③AuditMiddleware:审计操作

go 复制代码
// AuditMiddleware simulates an audit logging middleware.
func AuditMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("audit middleware...")
		next.ServeHTTP(w, r)
	})
}

④SmokeHandler:具体处理操作

go 复制代码
// SmokeHandler returns the current time as a string.
func SmokeHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("smoke handle....")
	_, err := w.Write([]byte(time.Now().String()))
	if err != nil {
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
	}
}

2 义中间件类型&定义中间件链

①type Middleware func(http.Handler) http.Handler:定义中间件

go 复制代码
type Middleware func(http.Handler) http.Handler

②定义中间件链MiddlewareChain

go 复制代码
// NewMiddlewareChain creates a new middleware chain with the given middlewares.
func NewMiddlewareChain(middlewares ...Middleware) Middleware {
	return func(handler http.Handler) http.Handler {
		for i := len(middlewares) - 1; i >= 0; i-- {
			handler = middlewares[i](handler)
		}
		return handler
	}
}

3 启动http服务

go 复制代码
func RunAndServe() error {
	defer func() {
		if e := recover(); e != nil {
			fmt.Println("err=", e)
		}
	}()

	mux := http.NewServeMux()

	// Create middleware chains for routes.
	authMiddlewareChain := NewMiddlewareChain(CORSMiddleware, AuthMiddleware, AuditMiddleware)
	//noAuthMiddlewareChain := NewMiddlewareChain(CORSMiddleware)

	// Convert the middleware chain result to http.HandlerFunc.
	smokeHandlerWrapped := func(w http.ResponseWriter, r *http.Request) {
		authMiddlewareChain(http.HandlerFunc(SmokeHandler)).ServeHTTP(w, r)
	}

	mux.HandleFunc("/smoke", smokeHandlerWrapped)

	fmt.Printf("listening on http://localhost:%d\n", 9999)
	return http.ListenAndServe(":9999", mux)
}

4 测试

  1. 启动后端
go 复制代码
go run main.go
  1. 浏览器访问http://localhost:9999/smoke

  2. 后端日志打印

可以看到是最后才处理我们的业务Handler

全部代码

🚀:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/middleware

go 复制代码
package main

import (
	"context"
	"fmt"
	"net/http"
	"time"
)

type Middleware func(http.Handler) http.Handler

// CORSMiddleware handles Cross-Origin Resource Sharing (CORS) responses.
func CORSMiddleware(next http.Handler) http.Handler {
	fmt.Println("cors middleware....")
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "OPTIONS" {
			w.Header().Set("Access-Control-Allow-Origin", "*")
			w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, DELETE")
			//如果前后端需要传递自定义请求头,需要再Access-Control-Allow-Headers中匹配(Yi-Auth-Token)
			w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, Yi-Auth-Token")
			w.WriteHeader(http.StatusOK)
			return
		}
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Accept,Yi-Auth-Token")
		next.ServeHTTP(w, r)
	})
}

// AuthMiddleware simulates a simple authentication middleware.
func AuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("auth middleware...")
		//store info in ctx
		token := r.Header.Get("Token")
		if len(token) != 0 {
			//TODO 1. check token 2. get userinfo from token
			userID := "1"
			ctx := context.WithValue(r.Context(), "userID", userID)
			r = r.WithContext(ctx)
		}
		next.ServeHTTP(w, r)
	})
}

// AuditMiddleware simulates an audit logging middleware.
func AuditMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("audit middleware...")
		next.ServeHTTP(w, r)
	})
}

// SmokeHandler returns the current time as a string.
func SmokeHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("smoke handle....")
	_, err := w.Write([]byte(time.Now().String()))
	if err != nil {
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
	}
}

// NewMiddlewareChain creates a new middleware chain with the given middlewares.
func NewMiddlewareChain(middlewares ...Middleware) Middleware {
	return func(handler http.Handler) http.Handler {
		for i := len(middlewares) - 1; i >= 0; i-- {
			handler = middlewares[i](handler)
		}
		return handler
	}
}

func RunAndServe() error {
	defer func() {
		if e := recover(); e != nil {
			fmt.Println("err=", e)
		}
	}()

	mux := http.NewServeMux()

	// Create middleware chains for routes.
	authMiddlewareChain := NewMiddlewareChain(CORSMiddleware, AuthMiddleware, AuditMiddleware)
	//noAuthMiddlewareChain := NewMiddlewareChain(CORSMiddleware)

	// Convert the middleware chain result to http.HandlerFunc.
	smokeHandlerWrapped := func(w http.ResponseWriter, r *http.Request) {
		authMiddlewareChain(http.HandlerFunc(SmokeHandler)).ServeHTTP(w, r)
	}

	mux.HandleFunc("/smoke", smokeHandlerWrapped)

	fmt.Printf("listening on http://localhost:%d\n", 9999)
	return http.ListenAndServe(":9999", mux)
}

func main() {
	RunAndServe()
}
相关推荐
花酒锄作田2 小时前
Go - 注册表工厂模式的简单实现
golang
圣殿骑士-Khtangc13 小时前
Go-Map面试核心考点从哈希冲突到并发安全的完整解析
面试·golang·哈希算法
worxfr13 小时前
Go 并发控制:从 Channel 方向约束到实战模式
开发语言·后端·golang
xcLeigh14 小时前
Go入门:短变量声明的陷阱与最佳实践
java·redis·golang·教程·变量
JavaPub-rodert14 小时前
我又把自己的 Go 后台管理系统升级了一遍:文件管理、2GB 上传、私有文件预览、Docker 镜像全安排上了
开发语言·docker·golang·shiyuadmin
名字还没想好☜15 小时前
Go 的 sync.Cond 实战:用条件变量做等待/通知,比忙轮询省 CPU
开发语言·数据库·后端·golang·go
Misnearch15 小时前
MCP以及底层协议、传输模式
http·mcp·json-rpc
程序员良辰16 小时前
【TongWeb8】使用 Crontab 定时重启和检测 TongWeb 服务
java·中间件·tomcat
圣殿骑士-Khtangc17 小时前
Go-Select多路复用机制的面试真题与底层实现
开发语言·面试·golang
IT白小白19 小时前
每日问答中间件篇
中间件