一、中间件的概念
在go语言中,中间件是一种用于处理http请求的开发模式,允许开发人员在请求到达处理程序之前或之后执行特定的操作,如日志记录、身份验证、错误处理等。
中间件通常是一个函数,它接收一个 `http.Handler` 作为参数并返回另一个 `http.Handler`。
go源码中 Handler 的定义如下:
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
二、go原生http中使用中间件的方法
使用方法:
1、创建中间件函数,输入参数 http.Handler,输出参数 http.Handler
2、将处理器函数作为参数传入上述中间件函数
3、运用 Handle(pattern string, handler Handler) 等函数将中间件函数绑定到请求路由中
代码示例:
package main import ( "fmt" "log" "net/http" ) // 日志记录中间件 func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Printf("请求处理之前 Request URI: %s\n", r.RequestURI) next.ServeHTTP(w, r) log.Printf("处理程序之后进行的操作...\n") }) } // handler 处理函数 func SayHello(w http.ResponseWriter, r *http.Request) { fmt.Println("Hello world") } func main() { //创建一个 HTTP 请求路由器 mux := http.NewServeMux() // 使用日志记录中间件 mux.Handle("/index", loggingMiddleware(http.HandlerFunc(SayHello))) //启动服务,使用创建的 ServeMux http.ListenAndServe(":8081", mux) }
运行效果:
三、go微服务框架Kratos使用中间件的方法
kratos介绍中间件的官网地址:概览 | Kratos
1、内置中间件使用方法
①通过 Middleware(m ...middleware.Middleware) ServerOption 添加所需的中间件
②将上述由中间件组成的 ServerOption 添加到 []http.ServerOption 中
③通过函数 NewServer(opts ...ServerOption) *Server 创建 httpServer
代码示例:
func NewHTTPServer(c *conf.Server, logger log.Logger, blog *service.BlogService) *http.Server { opts := []http.ServerOption{ http.Middleware( //使用kratos内置中间件 recovery.Recovery(), tracing.Server(), logging.Server(logger), validate.Validator(), ), } if c.Http.Network != "" { opts = append(opts, http.Network(c.Http.Network)) } if c.Http.Addr != "" { opts = append(opts, http.Address(c.Http.Addr)) } if c.Http.Timeout != nil { opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration())) } //创建 httpServer srv := http.NewServer(opts...) v1.RegisterBlogServiceHTTPServer(srv, blog) return srv }
2、自定义中间件使用方法
①实现 middleware.Middleware 接口,其定义如下:
// Handler defines the handler invoked by Middleware. type Handler func(ctx context.Context, req interface{}) (interface{}, error) // Middleware is HTTP/gRPC transport middleware. type Middleware func(Handler) Handler
中间件中您可以使用 tr, ok := transport.FromServerContext(ctx)
获得 Transporter 实例以便访问接口相关的元信息。
②像添加内置中间件一样,将自定义中间件添加到 http.Middleware 和 []http.ServerOption 中
③通过函数 NewServer(opts ...ServerOption) *Server 创建 httpServer
代码示例:
// auth.go 单元 //自定义 JWT 认证中间件 func JWTAuth(jwtSecret string) middleware.Middleware { return func(handler middleware.Handler) middleware.Handler { return func(ctx context.Context, req interface{}) (reply interface{}, err error) { if tr, ok := transport.FromServerContext(ctx); ok { tokenString := tr.RequestHeader().Get("authorization") spew.Dump(tokenString) //token, err := jwt.ParseWithClaims(tokenString, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { // Don't forget to validate the alg is what you expect: if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) } // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") return []byte(jwtSecret), nil }) if err != nil { log.Fatal(err) } if claims, ok := token.Claims.(jwt.MapClaims); ok { fmt.Println(claims["iss"], claims["exp"]) //fmt.Println(claims.Issuer, claims.ExpiresAt) } else { fmt.Println(err) } } return handler(ctx, req) } } } // server/http.go 单元 func NewHTTPServer(c *conf.Server, confAuth *conf.Auth, logger log.Logger, blog *service.BlogService) *http.Server { opts := []http.ServerOption{ http.Middleware( //自定义中间件 JWTAuth auth.JWTAuth(confAuth.JwtSecrect), ), } if c.Http.Network != "" { opts = append(opts, http.Network(c.Http.Network)) } if c.Http.Addr != "" { opts = append(opts, http.Address(c.Http.Addr)) } if c.Http.Timeout != nil { opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration())) } //创建 httpServer srv := http.NewServer(opts...) v1.RegisterBlogServiceHTTPServer(srv, blog) return srv }
3、为特定路由定制中间件的使用方法
使用方法:
对特定路由定制中间件:
server: selector.Server(ms...)
client: selector.Client(ms...)
代码示例:
// server/http.go 单元 // 添加验证白名单 func NewWhiteListMatcher() selector.MatchFunc { whiteList := make(map[string]struct{}) whiteList["/blog.api.v1.Account/Login"] = struct{}{} whiteList["/blog.api.v1.Account/Register"] = struct{}{} return func(ctx context.Context, operation string) bool { if _, ok := whiteList[operation]; ok { return false } return true } } // server/http.go 单元 func NewHTTPServer(c *conf.Server, confAuth *conf.Auth, logger log.Logger, blog *service.BlogService) *http.Server { opts := []http.ServerOption{ http.Middleware( //自定义中间件 JWTAuth selector.Server(auth.JWTAuth(confAuth.JwtSecrect)). Match(NewWhiteListMatcher()). Build(), ), } if c.Http.Network != "" { opts = append(opts, http.Network(c.Http.Network)) } if c.Http.Addr != "" { opts = append(opts, http.Address(c.Http.Addr)) } if c.Http.Timeout != nil { opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration())) } //创建 httpServer srv := http.NewServer(opts...) v1.RegisterBlogServiceHTTPServer(srv, blog) return srv }
注意: 定制中间件是通过 operation 匹配,并不是 http 本身的路由!!!
gRPC path 的拼接规则为 /包名.服务名/方法名(/package.Service/Method)
。