目录

go中间件学习

go中间件学习

本博文源于笔者正在学习go中间件,罗列了较为常用的中间件,例如日志记录、认证授权、跨域资源共享、请求体解析、静态文件处理、错误处理、性能分析、速率限制、session

1、日志记录中间件

可以追加打印用,例如,将请求进行打印 func logMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Printf(“Request: %s %s\n”, r.Method, r.URL) next.ServeHTTP(w, r) }) }

2、认证和授权中间件

对请求进行认证。 func authMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get(“API-Key”) != “my-secret-key” { http.Error(w, “Unauthorized”, http.StatusUnauthorized) return } next.ServeHTTP(w, r) }) }

3、跨域资源共享中间件

import “github.com/rs/cors” func handler(w http.ResponseWriter, r http.Request) { w.Write([]byte(“Hello World”)) } func main() { fmt.Println(“http://127.0.0.1:8082/”) mux := http.NewServeMux() mux.HandleFunc("/", handler) c := cors.New(cors.Options{ AllowedOrigins: []string{""}, AllowedMethods: []string{“GET”, “POST”, “PUT”, “DELETE”}, AllowedHeaders: []string{“Content-Type”}, }) handlerWithCors := c.Handler(mux) log.Fatal(http.ListenAndServe(":8082", handlerWithCors)) }

4、请求体解析中间件

对请求体进行解析 package main import ( “encoding/json” “fmt” “log” “net/http” ) type Data struct { Name string json:"name" Value string json:"value" } func handler(w http.ResponseWriter, r *http.Request) { var data Data err := json.NewDecoder(r.Body).Decode(&data) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } response := map[string]interface{}{ “message”: “Hello " + data.Name + “!”, “data”: data, } w.Header().Set(“Content-Type”, “application/json”) json.NewEncoder(w).Encode(response) } func main() { fmt.Println(“http://127.0.0.1:3000/api/data”) //通过json包解析请求体中的json数据,并将其 http.HandleFunc("/api/data”, handler) log.Fatal(http.ListenAndServe(":3000", nil)) }

5、静态文件处理中间件

对服务器下的文件进行请求 package main import ( “fmt” “log” “net/http” ) func main() { //提供static文件夹中的静态文件,用户可以通过访问下面的网站进行访问资源 fmt.Println(“http://127.0.0.1:3001/static/demo.png”) fs := http.FileServer(http.Dir(“static”)) http.Handle("/static/", http.StripPrefix("/static/", fs)) log.Fatal(http.ListenAndServe(":3001", nil)) }

6、错误处理中间件

遇到的错误可以方便进行recover处理 package main import ( “fmt” “log” “net/http” ) // 错误处理中间件 func errorHandlingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if err := recover(); err != nil { http.Error(w, fmt.Sprintf(“Internal Server Error: %v”, err), http.StatusInternalServerError) } }() next.ServeHTTP(w, r) }) } func handler(w http.ResponseWriter, r *http.Request) { panic(“Something went wrong!”) // 模拟错误 } func main() { //通过panic与recover捕捉错误 fmt.Println(“http://127.0.0.1:3002/panic”) mux := http.NewServeMux() mux.HandleFunc("/panic", handler) http.Handle("/", errorHandlingMiddleware(mux)) log.Fatal(http.ListenAndServe(":3002", nil)) }

7、性能分析中间件

时间运行时统计的一个中间件 package main import ( “fmt” “log” “net/http” “time” ) func performanceMonitoringMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() next.ServeHTTP(w, r) duration := time.Since(start) fmt.Printf(“Request took %v\n”, duration) }) } func handler(w http.ResponseWriter, r *http.Request) { time.Sleep(2 * time.Second) w.Write([]byte(“Hello World”)) } func main() { //记录每个请求的处理时间,并输出在控制台。 fmt.Println(“http://127.0.0.1:3003”) mux := http.NewServeMux() mux.HandleFunc("/", handler) http.Handle("/", performanceMonitoringMiddleware(mux)) log.Fatal(http.ListenAndServe(":3003", nil)) }

8、缓存中间件

对缓存进行redis写入 package main import ( “context” “fmt” “github.com/go-redis/redis/v8” “log” “net/http” “time” ) var rdb *redis.Client func initRedis() { rdb = redis.NewClient(&redis.Options{ Addr: “localhost:6379”, }) } func cacheMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r http.Request) { key := r.URL.Path cachedValue, err := rdb.Get(context.Background(), key).Result() fmt.Println("!!!!!!", cachedValue, “??”, err, “????”, key) if err == nil { fmt.Println(“Cache hit!”) w.Write([]byte(cachedValue)) return } fmt.Println(“Cache miss.”) next.ServeHTTP(w, r) rdb.Set(context.Background(), key, “This is the cached response”, 60time.Second) }) } func handler(w http.ResponseWriter, r *http.Request) { time.Sleep(1 * time.Second) w.Write([]byte(“hello from the server”)) } func main() { initRedis() fmt.Println(“http://127.0.0.1:3005”) mux := http.NewServeMux() mux.HandleFunc("/", handler) http.Handle("/", cacheMiddleware(mux)) log.Fatal(http.ListenAndServe(":3005", nil)) }

9、速率限制中间件

对速率限制,即对请求次数进行统计 package main import ( “context” “fmt” “github.com/go-redis/redis/v8” “log” “net/http” “time” ) var rdb *redis.Client func initRedis() { rdb = redis.NewClient(&redis.Options{ Addr: “localhost:6379”, }) } func rateLimitMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r http.Request) { ip := r.RemoteAddr key := “rate_limit:” + ip count, err := rdb.Get(context.Background(), key).Int() if err != nil && err != redis.Nil { http.Error(w, “Internal Server Error”, http.StatusInternalServerError) return } if count >= 5 { http.Error(w, “Too many requests”, http.StatusTooManyRequests) return } rdb.Incr(context.Background(), key) rdb.Expire(context.Background(), key, 1time.Minute) next.ServeHTTP(w, r) }) } func handler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(“hello from the server”)) } func main() { initRedis() fmt.Println(“http://127.0.0.1:3005”) mux := http.NewServeMux() mux.HandleFunc("/", handler) http.Handle("/", rateLimitMiddleware(mux)) log.Fatal(http.ListenAndServe(":3005", nil)) }

10、session中间件

通过第三方包来管理用户会话,中间件检查用户是否已通过身份验证,只有通过验证的用户才可以访问保护的页面 package main import ( “fmt” “log” “net/http” ) import “github.com/gorilla/sessions” //通过三包来管理用户会话,中间件检查用户是否已通过身份验证,只有通过验证的用户才可以访问保护的页面 var store = sessions.NewCookieStore([]byte(“my-secret-key”)) func sessionMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { session, _ := store.Get(r, “session-name”) //检查session是否存在 if session.Values[“authenticated”] == true { next.ServeHTTP(w, r) } else { http.Error(w, “Forbidden”, http.StatusForbidden) } }) } func loginHandler(w http.ResponseWriter, r *http.Request) { session, _ := store.Get(r, “session-name”) session.Values[“authenticated”] = true session.Save(r, w) fmt.Fprintln(w, “You are logged in!”) } func main() { fmt.Println(“http://127.0.0.1:8082/login”) fmt.Println(“http://127.0.0.1:8082/protected”) mux := http.NewServeMux() mux.HandleFunc("/login", loginHandler) mux.Handle("/protected", sessionMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, “You have access to this page”) }))) log.Fatal(http.ListenAndServe(":8082", mux)) }

11、请求数据验证中间件

对数据进行验证 package main import ( “encoding/json” “fmt” “github.com/go-playground/validator/v10” “log” “net/http” ) // 用户结构体,用于验证 type User struct { Name string json:"name" validate:"required" Email string json:"email" validate:"required,email" } // 数据验证中间件 func validationMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 解析请求体中的 JSON 数据 var user User decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&user); err != nil { http.Error(w, “Invalid request body”, http.StatusBadRequest) return } // 验证数据 validate := validator.New() if err := validate.Struct(user); err != nil { http.Error(w, fmt.Sprintf(“Validation failed: %v”, err), http.StatusBadRequest) return } // 验证通过,继续处理请求 next.ServeHTTP(w, r) }) } func handler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(“User data is valid!”)) } func main() { mux := http.NewServeMux() mux.HandleFunc("/user", handler) // 包装请求处理器,加入数据验证中间件 http.Handle("/user", validationMiddleware(mux)) log.Fatal(http.ListenAndServe(":3000", nil)) }

12、压缩中间件

对请求头进行查阅,观察是否需要压缩 package main import ( “compress/gzip” “fmt” “log” “net/http” “strings” ) func compressionMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.Contains(r.Header.Get(“Accept-Encoding”), “gzip”) { w.Header().Set(“Content-Encoding”, “gzip”) gz := gzip.NewWriter(w) defer gz.Close() gzw := &gzipResponseWriter{Writer: gz, ResponseWriter: w} next.ServeHTTP(gzw, r) } else { next.ServeHTTP(w, r) } }) } type gzipResponseWriter struct { http.ResponseWriter Writer *gzip.Writer } func (g *gzipResponseWriter) Write(b []byte) (int, error) { return g.Writer.Write(b) } func handler(w http.ResponseWriter, r *http.Request) { responseText := “This is a test response that will be compressed if the client supports Gzip.\n” for i := 0; i < 5; i++ { responseText += responseText } w.Write([]byte(responseText)) } func main() { fmt.Println(“http://127.0.0.1:8085/”) mux := http.NewServeMux() mux.HandleFunc("/", handler) http.Handle("/", compressionMiddleware(mux)) log.Fatal(http.ListenAndServe(":8085", nil)) }