Go 微服务框架 | 中间件

文章目录

    • 定义中间件
    • 前置中间件
    • 后置中间件
    • 路由级别中间件

定义中间件

  • 中间件的作用是给应用添加一些额外的功能,但是不会影响原有应用的编码方式,想用的时候直接添加,不想用的时候也可以轻松去除,实现所谓的可插拔。
  • 中间件的实现位置在哪里?
    • 不能耦合在用户的代码中
    • 需要独立存在,但是又能拿到上下文并作出影响
    • 位置:在处理器的前后
  • 注意:中间件是一个调用链条,所以在处理真正的业务之前可能会经过多个中间件。
// 定义中间件
type MiddlewareFunc func(handleFunc HandleFunc) HandleFunc

前置中间件

// zj.gopackage zjgoimport ("fmt""log""net/http"
)const ANY = "ANY"// 定义处理响应函数
type HandleFunc func(ctx *Context)// 定义中间件
type MiddlewareFunc func(handleFunc HandleFunc) HandleFunc// 抽象出路由的概念
type routerGroup struct {name             string                           // 组名handleFuncMap    map[string]map[string]HandleFunc // 映射关系应该由每个路由组去维护handlerMethodMap map[string][]string              // 记录GET,POST等请求方式所记录的路由,实现对同一路由不同请求方式的支持TreeNode         *TreeNode                        // 记录该路由组下的路由前缀树preMiddlewares   []MiddlewareFunc                 // 定义前置中间件postMiddlewares  []MiddlewareFunc                 // 定义后置中间件
}// 增加前置中间件
func (routerGroup *routerGroup) PreHandle(middlewareFunc ...MiddlewareFunc) { // 在Go语言中,函数参数中的三个点...表示可变参数(variadic parameter),允许函数接受不定数量的同类型参数。routerGroup.preMiddlewares = append(routerGroup.preMiddlewares, middlewareFunc...)
}// 增加后置中间件
func (routerGroup *routerGroup) PostHandle(middlewareFunc ...MiddlewareFunc) {routerGroup.postMiddlewares = append(routerGroup.postMiddlewares, middlewareFunc...)
}func (routerGroup *routerGroup) methodHandle(handle HandleFunc, ctx *Context) {// 执行前置中间件if routerGroup.preMiddlewares != nil {for _, middlewareFunc := range routerGroup.preMiddlewares {handle = middlewareFunc(handle)}}handle(ctx)// 执行后置中间件}// 定义路由结构体
type router struct {routerGroups []*routerGroup // 路由下面应该维护着不同的组
}// 添加路由组
func (r *router) Group(name string) *routerGroup {routerGroup := &routerGroup{name:             name,handleFuncMap:    make(map[string]map[string]HandleFunc),handlerMethodMap: make(map[string][]string),TreeNode:         &TreeNode{name: "/", children: make([]*TreeNode, 0)},}r.routerGroups = append(r.routerGroups, routerGroup)return routerGroup
}// 给路由结构体添加一个添加路由功能的函数
// func (routerGroup *routerGroup) Add(name string, handleFunc HandleFunc) {
// 	routerGroup.handleFuncMap[name] = handleFunc
// }// 由于 ANY POST GET 都需要重复相同的逻辑代码,所以做一个提取操作
func (routerGroup *routerGroup) handleRequest(name string, method string, handleFunc HandleFunc) {if _, exist := routerGroup.handleFuncMap[name]; !exist {routerGroup.handleFuncMap[name] = make(map[string]HandleFunc)}if _, exist := routerGroup.handleFuncMap[name][method]; !exist {routerGroup.handleFuncMap[name][method] = handleFuncrouterGroup.handlerMethodMap[method] = append(routerGroup.handlerMethodMap[method], name)} else {panic("Under the same route, duplication is not allowed!!!")}routerGroup.TreeNode.Put(name)
}// Any代表支持任意的请求方式
func (routerGroup *routerGroup) Any(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, ANY, handleFunc)
}// POST代表支持POST请求方式
func (routerGroup *routerGroup) Post(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodPost, handleFunc)
}// GET代表支持GET请求方式
func (routerGroup *routerGroup) Get(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodGet, handleFunc)
}// DELETE代表支持DELETE请求方式
func (routerGroup *routerGroup) Delete(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodDelete, handleFunc)
}// PUT代表支持PUT请求方式
func (routerGroup *routerGroup) Put(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodPut, handleFunc)
}// PATCH代表支持PATCH请求方式
func (routerGroup *routerGroup) Patch(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodPatch, handleFunc)
}// 只要实现 ServeHTTP 这个方法,就相当于实现了对应的 HTTP 处理器
// 结构体 Engine 实现了 ServeHTTP(w http.ResponseWriter, r *http.Request) 方法
// 所以它就自动实现了 http.Handler 接口,因此可以直接被用于 http.ListenAndServe
// Engine 实现了 ServeHTTP,它就是一个合法的 http.Handler,可以用 http.Handle 来绑定它到某个具体的路由路径上!
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {e.httpRequestHandle(w, r)
}func (e *Engine) httpRequestHandle(w http.ResponseWriter, r *http.Request) {// 在 Go 的 net/http 包中,r *http.Request 代表了客户端发来的 HTTP 请求对象。// 可以通过 r.Method 来获取这次请求使用的是什么方法(Method),例如:GET、POST、PUT、DELETE 等。if r.Method == http.MethodGet {fmt.Fprintf(w, "这是一个 GET 请求!!! ")} else if r.Method == http.MethodPost {fmt.Fprintf(w, "这是一个 POST 请求!!! ")} else {fmt.Fprintf(w, "这是一个其他类型的请求:%s!!! ", r.Method)}for _, group := range e.routerGroups {routerName := SubStringLast(r.RequestURI, "/"+group.name)if node := group.TreeNode.Get(routerName); node != nil && node.isEnd {// 路由匹配上了ctx := &Context{W: w, R: r}// 先判断当前请求路由是否支持任意请求方式的Anyif handle, exist := group.handleFuncMap[node.routerName][ANY]; exist {group.methodHandle(handle, ctx)return}// 不支持 Any,去该请求所对应的请求方式对应的 Map 里面去找是否有对应的路由if handle, exist := group.handleFuncMap[node.routerName][r.Method]; exist {group.methodHandle(handle, ctx)return}// 没找到对应的路由,说明该请求方式不允许w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allowed!!!\n", r.Method, r.RequestURI)return}}w.WriteHeader(http.StatusNotFound)fmt.Fprintf(w, "%s %s not found!!!\n", r.Method, r.RequestURI)return
}// 定义一个引擎结构体
type Engine struct {router
}// 引擎结构体的初始化方法
func New() *Engine {return &Engine{router: router{},}
}// 引擎的启动方法
func (e *Engine) Run() {// for _, group := range e.routerGroups {// 	for name, value := range group.handleFuncMap {// 		http.HandleFunc("/"+group.name+name, value)// 	}// }// 把 e 这个http处理器绑定到对应路由下http.Handle("/", e)err := http.ListenAndServe(":3986", nil)if err != nil {log.Fatal(err)}
}
// main.gopackage mainimport ("fmt""net/http""github.com/ErizJ/ZJGo/zjgo"
)func main() {fmt.Println("Hello World!")// // 注册 HTTP 路由 /hello// http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!")// })// // 启动 HTTP 服务器// err := http.ListenAndServe("8111", nil)// if err != nil {// 	log.Fatal(err)// }engine := zjgo.New()g1 := engine.Group("user")g1.PreHandle(func(handleFunc zjgo.HandleFunc) zjgo.HandleFunc {return func(ctx *zjgo.Context) {fmt.Println("Pre Middleware ON!!!")handleFunc(ctx)}})g1.Get("/hello", func(ctx *zjgo.Context) {fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/hello")})// 浏览器地址栏输入的都是 GET 请求// 需要用 curl 或 Postman 来发一个真正的 POST 请求,才会命中 Post handlerg1.Post("/info", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodPost+" Hello Go!——user/info——POST")})g1.Get("/info", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/info——GET")})g1.Get("/get/:id", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/get/:id——GET")})g1.Get("/isEnd/get", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/isEnd/get——GET")})// 只要路由匹配,就会执行对应的处理函数g1.Any("/any", func(ctx *zjgo.Context) {fmt.Fprintf(ctx.W, " Hello Go!——user/any")})// g2 := engine.Group("order")// g2.Add("/hello", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!——order/hello")// })// g2.Add("/info", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!——order/info")// })fmt.Println("Starting...")engine.Run()}

后置中间件

// zj.gopackage zjgoimport ("fmt""log""net/http"
)const ANY = "ANY"// 定义处理响应函数
type HandleFunc func(ctx *Context)// 定义中间件
type MiddlewareFunc func(handleFunc HandleFunc) HandleFunc// 抽象出路由的概念
type routerGroup struct {name             string                           // 组名handleFuncMap    map[string]map[string]HandleFunc // 映射关系应该由每个路由组去维护handlerMethodMap map[string][]string              // 记录GET,POST等请求方式所记录的路由,实现对同一路由不同请求方式的支持TreeNode         *TreeNode                        // 记录该路由组下的路由前缀树preMiddlewares   []MiddlewareFunc                 // 定义前置中间件postMiddlewares  []MiddlewareFunc                 // 定义后置中间件
}// 增加前置中间件
func (routerGroup *routerGroup) PreHandle(middlewareFunc ...MiddlewareFunc) { // 在Go语言中,函数参数中的三个点...表示可变参数(variadic parameter),允许函数接受不定数量的同类型参数。routerGroup.preMiddlewares = append(routerGroup.preMiddlewares, middlewareFunc...)
}// 增加后置中间件
func (routerGroup *routerGroup) PostHandle(middlewareFunc ...MiddlewareFunc) {routerGroup.postMiddlewares = append(routerGroup.postMiddlewares, middlewareFunc...)
}func (routerGroup *routerGroup) methodHandle(handle HandleFunc, ctx *Context) {// 执行前置中间件if routerGroup.preMiddlewares != nil {for _, middlewareFunc := range routerGroup.preMiddlewares {handle = middlewareFunc(handle) // 难以理解的话可以看作语句叠加}}handle(ctx)// 执行后置中间件if routerGroup.postMiddlewares != nil {for _, middlewareFunc := range routerGroup.postMiddlewares {handle = middlewareFunc(handle)}}handle(ctx)
}// 定义路由结构体
type router struct {routerGroups []*routerGroup // 路由下面应该维护着不同的组
}// 添加路由组
func (r *router) Group(name string) *routerGroup {routerGroup := &routerGroup{name:             name,handleFuncMap:    make(map[string]map[string]HandleFunc),handlerMethodMap: make(map[string][]string),TreeNode:         &TreeNode{name: "/", children: make([]*TreeNode, 0)},}r.routerGroups = append(r.routerGroups, routerGroup)return routerGroup
}// 给路由结构体添加一个添加路由功能的函数
// func (routerGroup *routerGroup) Add(name string, handleFunc HandleFunc) {
// 	routerGroup.handleFuncMap[name] = handleFunc
// }// 由于 ANY POST GET 都需要重复相同的逻辑代码,所以做一个提取操作
func (routerGroup *routerGroup) handleRequest(name string, method string, handleFunc HandleFunc) {if _, exist := routerGroup.handleFuncMap[name]; !exist {routerGroup.handleFuncMap[name] = make(map[string]HandleFunc)}if _, exist := routerGroup.handleFuncMap[name][method]; !exist {routerGroup.handleFuncMap[name][method] = handleFuncrouterGroup.handlerMethodMap[method] = append(routerGroup.handlerMethodMap[method], name)} else {panic("Under the same route, duplication is not allowed!!!")}routerGroup.TreeNode.Put(name)
}// Any代表支持任意的请求方式
func (routerGroup *routerGroup) Any(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, ANY, handleFunc)
}// POST代表支持POST请求方式
func (routerGroup *routerGroup) Post(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodPost, handleFunc)
}// GET代表支持GET请求方式
func (routerGroup *routerGroup) Get(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodGet, handleFunc)
}// DELETE代表支持DELETE请求方式
func (routerGroup *routerGroup) Delete(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodDelete, handleFunc)
}// PUT代表支持PUT请求方式
func (routerGroup *routerGroup) Put(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodPut, handleFunc)
}// PATCH代表支持PATCH请求方式
func (routerGroup *routerGroup) Patch(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodPatch, handleFunc)
}// 只要实现 ServeHTTP 这个方法,就相当于实现了对应的 HTTP 处理器
// 结构体 Engine 实现了 ServeHTTP(w http.ResponseWriter, r *http.Request) 方法
// 所以它就自动实现了 http.Handler 接口,因此可以直接被用于 http.ListenAndServe
// Engine 实现了 ServeHTTP,它就是一个合法的 http.Handler,可以用 http.Handle 来绑定它到某个具体的路由路径上!
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {e.httpRequestHandle(w, r)
}func (e *Engine) httpRequestHandle(w http.ResponseWriter, r *http.Request) {// 在 Go 的 net/http 包中,r *http.Request 代表了客户端发来的 HTTP 请求对象。// 可以通过 r.Method 来获取这次请求使用的是什么方法(Method),例如:GET、POST、PUT、DELETE 等。if r.Method == http.MethodGet {fmt.Fprintf(w, "这是一个 GET 请求!!! ")} else if r.Method == http.MethodPost {fmt.Fprintf(w, "这是一个 POST 请求!!! ")} else {fmt.Fprintf(w, "这是一个其他类型的请求:%s!!! ", r.Method)}for _, group := range e.routerGroups {routerName := SubStringLast(r.RequestURI, "/"+group.name)if node := group.TreeNode.Get(routerName); node != nil && node.isEnd {// 路由匹配上了ctx := &Context{W: w, R: r}// 先判断当前请求路由是否支持任意请求方式的Anyif handle, exist := group.handleFuncMap[node.routerName][ANY]; exist {group.methodHandle(handle, ctx)return}// 不支持 Any,去该请求所对应的请求方式对应的 Map 里面去找是否有对应的路由if handle, exist := group.handleFuncMap[node.routerName][r.Method]; exist {group.methodHandle(handle, ctx)return}// 没找到对应的路由,说明该请求方式不允许w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allowed!!!\n", r.Method, r.RequestURI)return}}w.WriteHeader(http.StatusNotFound)fmt.Fprintf(w, "%s %s not found!!!\n", r.Method, r.RequestURI)return
}// 定义一个引擎结构体
type Engine struct {router
}// 引擎结构体的初始化方法
func New() *Engine {return &Engine{router: router{},}
}// 引擎的启动方法
func (e *Engine) Run() {// for _, group := range e.routerGroups {// 	for name, value := range group.handleFuncMap {// 		http.HandleFunc("/"+group.name+name, value)// 	}// }// 把 e 这个http处理器绑定到对应路由下http.Handle("/", e)err := http.ListenAndServe(":3986", nil)if err != nil {log.Fatal(err)}
}
// main.gopackage mainimport ("fmt""net/http""github.com/ErizJ/ZJGo/zjgo"
)func main() {fmt.Println("Hello World!")// // 注册 HTTP 路由 /hello// http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!")// })// // 启动 HTTP 服务器// err := http.ListenAndServe("8111", nil)// if err != nil {// 	log.Fatal(err)// }engine := zjgo.New()g1 := engine.Group("user")g1.PreHandle(func(handleFunc zjgo.HandleFunc) zjgo.HandleFunc {return func(ctx *zjgo.Context) {fmt.Println("Pre Middleware ON!!!")handleFunc(ctx)}})g1.PostHandle(func(handleFunc zjgo.HandleFunc) zjgo.HandleFunc {return func(ctx *zjgo.Context) {fmt.Println("Post Middleware ON!!!")}})g1.Get("/hello", func(ctx *zjgo.Context) {fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/hello")})// 浏览器地址栏输入的都是 GET 请求// 需要用 curl 或 Postman 来发一个真正的 POST 请求,才会命中 Post handlerg1.Post("/info", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodPost+" Hello Go!——user/info——POST")})g1.Get("/info", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/info——GET")})g1.Get("/get/:id", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/get/:id——GET")})g1.Get("/isEnd/get", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/isEnd/get——GET")})// 只要路由匹配,就会执行对应的处理函数g1.Any("/any", func(ctx *zjgo.Context) {fmt.Fprintf(ctx.W, " Hello Go!——user/any")})// g2 := engine.Group("order")// g2.Add("/hello", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!——order/hello")// })// g2.Add("/info", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!——order/info")// })fmt.Println("Starting...")engine.Run()}
  • 但是这里前置和后置中间件似乎有点多余了,因为在前置中间件中,执行完一系列前置中间件和主体业务函数后,就可以执行后置中间件了,不用其他冗余代码。
// zj.gopackage zjgoimport ("fmt""log""net/http"
)const ANY = "ANY"// 定义处理响应函数
type HandleFunc func(ctx *Context)// 定义中间件
type MiddlewareFunc func(handleFunc HandleFunc) HandleFunc// 抽象出路由的概念
type routerGroup struct {name             string                           // 组名handleFuncMap    map[string]map[string]HandleFunc // 映射关系应该由每个路由组去维护handlerMethodMap map[string][]string              // 记录GET,POST等请求方式所记录的路由,实现对同一路由不同请求方式的支持TreeNode         *TreeNode                        // 记录该路由组下的路由前缀树middlewares      []MiddlewareFunc                 // 定义中间件
}// 增加中间件
func (routerGroup *routerGroup) UseMiddleware(middlewareFunc ...MiddlewareFunc) { // 在Go语言中,函数参数中的三个点...表示可变参数(variadic parameter),允许函数接受不定数量的同类型参数。routerGroup.middlewares = append(routerGroup.middlewares, middlewareFunc...)
}func (routerGroup *routerGroup) methodHandle(handle HandleFunc, ctx *Context) {// 执行通用中间件,任何路由组都可以执行该方法if routerGroup.middlewares != nil {for _, middlewareFunc := range routerGroup.middlewares {handle = middlewareFunc(handle) // 难以理解的话可以看作语句叠加}}handle(ctx)
}// 定义路由结构体
type router struct {routerGroups []*routerGroup // 路由下面应该维护着不同的组
}// 添加路由组
func (r *router) Group(name string) *routerGroup {routerGroup := &routerGroup{name:             name,handleFuncMap:    make(map[string]map[string]HandleFunc),handlerMethodMap: make(map[string][]string),TreeNode:         &TreeNode{name: "/", children: make([]*TreeNode, 0)},}r.routerGroups = append(r.routerGroups, routerGroup)return routerGroup
}// 给路由结构体添加一个添加路由功能的函数
// func (routerGroup *routerGroup) Add(name string, handleFunc HandleFunc) {
// 	routerGroup.handleFuncMap[name] = handleFunc
// }// 由于 ANY POST GET 都需要重复相同的逻辑代码,所以做一个提取操作
func (routerGroup *routerGroup) handleRequest(name string, method string, handleFunc HandleFunc) {if _, exist := routerGroup.handleFuncMap[name]; !exist {routerGroup.handleFuncMap[name] = make(map[string]HandleFunc)}if _, exist := routerGroup.handleFuncMap[name][method]; !exist {routerGroup.handleFuncMap[name][method] = handleFuncrouterGroup.handlerMethodMap[method] = append(routerGroup.handlerMethodMap[method], name)} else {panic("Under the same route, duplication is not allowed!!!")}routerGroup.TreeNode.Put(name)
}// Any代表支持任意的请求方式
func (routerGroup *routerGroup) Any(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, ANY, handleFunc)
}// POST代表支持POST请求方式
func (routerGroup *routerGroup) Post(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodPost, handleFunc)
}// GET代表支持GET请求方式
func (routerGroup *routerGroup) Get(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodGet, handleFunc)
}// DELETE代表支持DELETE请求方式
func (routerGroup *routerGroup) Delete(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodDelete, handleFunc)
}// PUT代表支持PUT请求方式
func (routerGroup *routerGroup) Put(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodPut, handleFunc)
}// PATCH代表支持PATCH请求方式
func (routerGroup *routerGroup) Patch(name string, handleFunc HandleFunc) {routerGroup.handleRequest(name, http.MethodPatch, handleFunc)
}// 只要实现 ServeHTTP 这个方法,就相当于实现了对应的 HTTP 处理器
// 结构体 Engine 实现了 ServeHTTP(w http.ResponseWriter, r *http.Request) 方法
// 所以它就自动实现了 http.Handler 接口,因此可以直接被用于 http.ListenAndServe
// Engine 实现了 ServeHTTP,它就是一个合法的 http.Handler,可以用 http.Handle 来绑定它到某个具体的路由路径上!
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {e.httpRequestHandle(w, r)
}func (e *Engine) httpRequestHandle(w http.ResponseWriter, r *http.Request) {// 在 Go 的 net/http 包中,r *http.Request 代表了客户端发来的 HTTP 请求对象。// 可以通过 r.Method 来获取这次请求使用的是什么方法(Method),例如:GET、POST、PUT、DELETE 等。if r.Method == http.MethodGet {fmt.Fprintf(w, "这是一个 GET 请求!!! ")} else if r.Method == http.MethodPost {fmt.Fprintf(w, "这是一个 POST 请求!!! ")} else {fmt.Fprintf(w, "这是一个其他类型的请求:%s!!! ", r.Method)}for _, group := range e.routerGroups {routerName := SubStringLast(r.RequestURI, "/"+group.name)if node := group.TreeNode.Get(routerName); node != nil && node.isEnd {// 路由匹配上了ctx := &Context{W: w, R: r}// 先判断当前请求路由是否支持任意请求方式的Anyif handle, exist := group.handleFuncMap[node.routerName][ANY]; exist {group.methodHandle(handle, ctx)return}// 不支持 Any,去该请求所对应的请求方式对应的 Map 里面去找是否有对应的路由if handle, exist := group.handleFuncMap[node.routerName][r.Method]; exist {group.methodHandle(handle, ctx)return}// 没找到对应的路由,说明该请求方式不允许w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allowed!!!\n", r.Method, r.RequestURI)return}}w.WriteHeader(http.StatusNotFound)fmt.Fprintf(w, "%s %s not found!!!\n", r.Method, r.RequestURI)return
}// 定义一个引擎结构体
type Engine struct {router
}// 引擎结构体的初始化方法
func New() *Engine {return &Engine{router: router{},}
}// 引擎的启动方法
func (e *Engine) Run() {// for _, group := range e.routerGroups {// 	for name, value := range group.handleFuncMap {// 		http.HandleFunc("/"+group.name+name, value)// 	}// }// 把 e 这个http处理器绑定到对应路由下http.Handle("/", e)err := http.ListenAndServe(":3986", nil)if err != nil {log.Fatal(err)}
}
// main.gopackage mainimport ("fmt""net/http""github.com/ErizJ/ZJGo/zjgo"
)func main() {fmt.Println("Hello World!")// // 注册 HTTP 路由 /hello// http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!")// })// // 启动 HTTP 服务器// err := http.ListenAndServe("8111", nil)// if err != nil {// 	log.Fatal(err)// }engine := zjgo.New()g1 := engine.Group("user")g1.UseMiddleware(func(handleFunc zjgo.HandleFunc) zjgo.HandleFunc {return func(ctx *zjgo.Context) {fmt.Println("Pre Middleware ON!!!")handleFunc(ctx)fmt.Println("Post Middleware ON!!!")}})g1.Get("/hello", func(ctx *zjgo.Context) {fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/hello")})// 浏览器地址栏输入的都是 GET 请求// 需要用 curl 或 Postman 来发一个真正的 POST 请求,才会命中 Post handlerg1.Post("/info", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodPost+" Hello Go!——user/info——POST")})g1.Get("/info", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/info——GET")})g1.Get("/get/:id", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/get/:id——GET")})g1.Get("/isEnd/get", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/isEnd/get——GET")})// 只要路由匹配,就会执行对应的处理函数g1.Any("/any", func(ctx *zjgo.Context) {fmt.Fprintf(ctx.W, " Hello Go!——user/any")})// g2 := engine.Group("order")// g2.Add("/hello", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!——order/hello")// })// g2.Add("/info", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!——order/info")// })fmt.Println("Starting...")engine.Run()}

路由级别中间件

// zj.gopackage zjgoimport ("fmt""log""net/http"
)const ANY = "ANY"// 定义处理响应函数
type HandleFunc func(ctx *Context)// 定义中间件
type MiddlewareFunc func(handleFunc HandleFunc) HandleFunc// 抽象出路由的概念
type routerGroup struct {name               string                                 // 组名handleFuncMap      map[string]map[string]HandleFunc       // 映射关系应该由每个路由组去维护handlerMethodMap   map[string][]string                    // 记录GET,POST等请求方式所记录的路由,实现对同一路由不同请求方式的支持middlewaresFuncMap map[string]map[string][]MiddlewareFunc // 定义路由级别中间件TreeNode           *TreeNode                              // 记录该路由组下的路由前缀树middlewares        []MiddlewareFunc                       // 定义通用中间件
}// 增加中间件
func (routerGroup *routerGroup) UseMiddleware(middlewareFunc ...MiddlewareFunc) { // 在Go语言中,函数参数中的三个点...表示可变参数(variadic parameter),允许函数接受不定数量的同类型参数。routerGroup.middlewares = append(routerGroup.middlewares, middlewareFunc...)
}func (routerGroup *routerGroup) methodHandle(name string, method string, handle HandleFunc, ctx *Context) {// 执行组通用中间件,任何路由组都可以执行该方法if routerGroup.middlewares != nil {for _, middlewareFunc := range routerGroup.middlewares {handle = middlewareFunc(handle) // 难以理解的话可以看作语句叠加}}// 路由级别组中间件if _, exist := routerGroup.middlewaresFuncMap[name][method]; exist {for _, middlewareFunc := range routerGroup.middlewaresFuncMap[name][method] {handle = middlewareFunc(handle)}}handle(ctx)
}// 定义路由结构体
type router struct {routerGroups []*routerGroup // 路由下面应该维护着不同的组
}// 添加路由组
func (r *router) Group(name string) *routerGroup {routerGroup := &routerGroup{name:               name,handleFuncMap:      make(map[string]map[string]HandleFunc),handlerMethodMap:   make(map[string][]string),middlewaresFuncMap: make(map[string]map[string][]MiddlewareFunc, 0),TreeNode:           &TreeNode{name: "/", children: make([]*TreeNode, 0)},}r.routerGroups = append(r.routerGroups, routerGroup)return routerGroup
}// 给路由结构体添加一个添加路由功能的函数
// func (routerGroup *routerGroup) Add(name string, handleFunc HandleFunc) {
// 	routerGroup.handleFuncMap[name] = handleFunc
// }// 由于 ANY POST GET 都需要重复相同的逻辑代码,所以做一个提取操作
// 组册路由
func (routerGroup *routerGroup) registerRoute(name string, method string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {if _, exist := routerGroup.handleFuncMap[name]; !exist {routerGroup.handleFuncMap[name] = make(map[string]HandleFunc)routerGroup.middlewaresFuncMap[name] = make(map[string][]MiddlewareFunc)}if _, exist := routerGroup.handleFuncMap[name][method]; !exist {routerGroup.handleFuncMap[name][method] = handleFuncrouterGroup.handlerMethodMap[method] = append(routerGroup.handlerMethodMap[method], name)routerGroup.middlewaresFuncMap[name][method] = append(routerGroup.middlewaresFuncMap[name][method], middlewareFunc...)} else {panic("Under the same route, duplication is not allowed!!!")}routerGroup.TreeNode.Put(name)
}// Any代表支持任意的请求方式
func (routerGroup *routerGroup) Any(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {routerGroup.registerRoute(name, ANY, handleFunc, middlewareFunc...)
}// POST代表支持POST请求方式
func (routerGroup *routerGroup) Post(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {routerGroup.registerRoute(name, http.MethodPost, handleFunc, middlewareFunc...)
}// GET代表支持GET请求方式
func (routerGroup *routerGroup) Get(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {routerGroup.registerRoute(name, http.MethodGet, handleFunc, middlewareFunc...)
}// DELETE代表支持DELETE请求方式
func (routerGroup *routerGroup) Delete(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {routerGroup.registerRoute(name, http.MethodDelete, handleFunc, middlewareFunc...)
}// PUT代表支持PUT请求方式
func (routerGroup *routerGroup) Put(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {routerGroup.registerRoute(name, http.MethodPut, handleFunc, middlewareFunc...)
}// PATCH代表支持PATCH请求方式
func (routerGroup *routerGroup) Patch(name string, handleFunc HandleFunc, middlewareFunc ...MiddlewareFunc) {routerGroup.registerRoute(name, http.MethodPatch, handleFunc, middlewareFunc...)
}// 只要实现 ServeHTTP 这个方法,就相当于实现了对应的 HTTP 处理器
// 结构体 Engine 实现了 ServeHTTP(w http.ResponseWriter, r *http.Request) 方法
// 所以它就自动实现了 http.Handler 接口,因此可以直接被用于 http.ListenAndServe
// Engine 实现了 ServeHTTP,它就是一个合法的 http.Handler,可以用 http.Handle 来绑定它到某个具体的路由路径上!
func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {e.httpRequestHandle(w, r)
}func (e *Engine) httpRequestHandle(w http.ResponseWriter, r *http.Request) {// 在 Go 的 net/http 包中,r *http.Request 代表了客户端发来的 HTTP 请求对象。// 可以通过 r.Method 来获取这次请求使用的是什么方法(Method),例如:GET、POST、PUT、DELETE 等。if r.Method == http.MethodGet {fmt.Fprintf(w, "这是一个 GET 请求!!! ")} else if r.Method == http.MethodPost {fmt.Fprintf(w, "这是一个 POST 请求!!! ")} else {fmt.Fprintf(w, "这是一个其他类型的请求:%s!!! ", r.Method)}for _, group := range e.routerGroups {routerName := SubStringLast(r.RequestURI, "/"+group.name)if node := group.TreeNode.Get(routerName); node != nil && node.isEnd {// 路由匹配上了ctx := &Context{W: w, R: r}// 先判断当前请求路由是否支持任意请求方式的Anyif handle, exist := group.handleFuncMap[node.routerName][ANY]; exist {group.methodHandle(node.routerName, ANY, handle, ctx)return}// 不支持 Any,去该请求所对应的请求方式对应的 Map 里面去找是否有对应的路由if handle, exist := group.handleFuncMap[node.routerName][r.Method]; exist {group.methodHandle(node.routerName, r.Method, handle, ctx)return}// 没找到对应的路由,说明该请求方式不允许w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allowed!!!\n", r.Method, r.RequestURI)return}}w.WriteHeader(http.StatusNotFound)fmt.Fprintf(w, "%s %s not found!!!\n", r.Method, r.RequestURI)return
}// 定义一个引擎结构体
type Engine struct {router
}// 引擎结构体的初始化方法
func New() *Engine {return &Engine{router: router{},}
}// 引擎的启动方法
func (e *Engine) Run() {// for _, group := range e.routerGroups {// 	for name, value := range group.handleFuncMap {// 		http.HandleFunc("/"+group.name+name, value)// 	}// }// 把 e 这个http处理器绑定到对应路由下http.Handle("/", e)err := http.ListenAndServe(":3986", nil)if err != nil {log.Fatal(err)}
}
// main.go
package mainimport ("fmt""net/http""github.com/ErizJ/ZJGo/zjgo"
)func Log(handleFunc zjgo.HandleFunc) zjgo.HandleFunc {return func(ctx *zjgo.Context) {fmt.Println("[LOG] Middleware START")handleFunc(ctx)fmt.Println("[LOG] Middleware END")}
}func main() {fmt.Println("Hello World!")// // 注册 HTTP 路由 /hello// http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!")// })// // 启动 HTTP 服务器// err := http.ListenAndServe("8111", nil)// if err != nil {// 	log.Fatal(err)// }engine := zjgo.New()g1 := engine.Group("user")g1.UseMiddleware(func(handleFunc zjgo.HandleFunc) zjgo.HandleFunc {return func(ctx *zjgo.Context) {fmt.Println("Pre Middleware ON!!!")handleFunc(ctx)fmt.Println("Post Middleware ON!!!")}})g1.Get("/hello", func(ctx *zjgo.Context) {fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/hello")})// 浏览器地址栏输入的都是 GET 请求// 需要用 curl 或 Postman 来发一个真正的 POST 请求,才会命中 Post handlerg1.Post("/info", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodPost+" Hello Go!——user/info——POST")})g1.Get("/info", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/info——GET")}, Log)g1.Get("/get/:id", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/get/:id——GET")})g1.Get("/isEnd/get", func(ctx *zjgo.Context) {fmt.Println("HandleFunc ON!!!")fmt.Fprintf(ctx.W, http.MethodGet+" Hello Go!——user/isEnd/get——GET")})// 只要路由匹配,就会执行对应的处理函数g1.Any("/any", func(ctx *zjgo.Context) {fmt.Fprintf(ctx.W, " Hello Go!——user/any")})// g2 := engine.Group("order")// g2.Add("/hello", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!——order/hello")// })// g2.Add("/info", func(w http.ResponseWriter, r *http.Request) {// 	fmt.Fprintf(w, "Hello Go!——order/info")// })fmt.Println("Starting...")engine.Run()
}// 注意执行顺序,理解包装这个词的意思,把函数作为一个整体再往表面包装的这个概念!
[LOG] Middleware START
Pre Middleware ON!!!
HandleFunc ON!!!
Post Middleware ON!!!
[LOG] Middleware END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/901336.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode 198. House Robber

本题是动态规划问题。 第一步,明确并理解dp数组以及下标的含义 dp[i]表示从第0号房间一直到第i号房间(包含第i号房间)可以偷到的最大金额,具体怎么偷这里不考虑,第i1号及之后的房间也不考虑。换句话说,dp[i]也就是只考虑[0,i]号…

掌趣科技前端面试题及参考答案

你使用 Vue 的频率如何,用得比较多吗? 在前端开发工作中,我对 Vue 的使用较为频繁。Vue 作为一款轻量级、易于上手且功能强大的前端框架,在众多项目里都发挥着重要作用。 在日常的项目里,Vue 的组件化开发特性为我带来了极大的便利。组件化能够将页面拆分成多个小的、可复…

深入解析Python爬虫技术:从基础到实战的功能工具开发指南

一、引言:Python 爬虫技术的核心价值 在数据驱动的时代,网络爬虫作为获取公开数据的重要工具,正发挥着越来越关键的作用。Python 凭借其简洁的语法、丰富的生态工具以及强大的扩展性,成为爬虫开发的首选语言。根据 Stack Overflow 2024 年开发者调查,68% 的专业爬虫开发者…

CSS 笔记——Flexbox(弹性盒布局)

目录 1. Flex 容器与 Flex 项目 2. 主轴与交叉轴 3. Flex 容器的属性 display flex-direction justify-content align-items align-content flex-wrap 4. Flex 项目的属性 flex-grow flex-shrink flex-basis flex align-self 5. Flexbox 的优点 6. Flexbox 的…

Java学习手册:Java反射与注解

Java反射(Reflection)和注解(Annotation)是Java语言中两个强大的特性,它们在框架开发和复杂应用中扮演着重要角色。反射允许程序在运行时检查和操作类、对象、接口、字段和方法,而注解则提供了一种元数据形…

JavaWeb遇到的问题汇总

问题一:(键值对最后一项没有逗号) 在JSON字符串转自定义对象和自定义对象转JSON字符串时: 如图所示:若忘记删除键值对的最后一项没有逗号时,则下一句转换不会生效,应该删除最后一项的逗号。 解…

模板引擎语法-变量

模板引擎语法-变量 文章目录 模板引擎语法-变量(一)在Django框架模板中使用变量的代码实例(二)在Django框架模板中使用变量对象属性的代码实例(三)在Django框架模板中使用变量显示列表 (一&…

AUTO-RAG: AUTONOMOUS RETRIEVAL-AUGMENTED GENERATION FOR LARGE LANGUAGE MODELS

Auto-RAG:用于大型语言模型的自主检索增强生成 单位:中科院计算所 代码: https://github.com/ictnlp/Auto-RAG 拟解决问题:通过手动构建规则或者few-shot prompting产生的额外推理开销。 贡献:提出一种以LLM决策为中…

Python 基础语法汇总

Python 语法 │ ├── 基本结构 │ ├── 语句(Statements) │ │ ├── 表达式语句(如赋值、算术运算) │ │ ├── 控制流语句(if, for, while) │ │ ├── 定义语句(def…

一文详解ffmpeg环境搭建:Ubuntu系统ffmpeg配置nvidia硬件加速

在Ubuntu系统下安装FFmpeg有多种方式,其中最常用的是通过apt-get命令和源码编译安装。本文将分别介绍这两种方式,并提供安装过程。 一、apt-get安装 使用apt-get命令安装FFmpeg是最简单快捷的方式,只需要在终端中输入以下命令即可: # 更新软件包列表 sudo apt-get updat…

Android 14 、15动态申请读写权限实现 (Java)

在 Android 14、15 中&#xff0c;Google 进一步优化了存储权限系统&#xff0c;特别是写权限的管理。以下是完整的 Java 实现方案&#xff1a; 1. AndroidManifest.xml 声明权限 <!-- Android 14 存储权限 --> <uses-permission android:name"android.permiss…

小刚说C语言刷题——第23讲 字符数组

前面&#xff0c;我们学习了一维数组和二维数组的概念。今天我们学习一种特殊的数组&#xff0c;字符数组。 1.字符数组的概念 字符数组就是指元素类型为字符的数组。字符数组是用来存放字符序列或者字符串的。 2.字符数组的定义及语法 char ch[5]; 3.字符数组的初始化及赋…

用AI生成系统架构图

DeepSeek+Drawio+SVG绘制架构图-找到一种真正可行实用的方法和思路 1、使用DeepSeek生成SVG文件,导入drawio工具的方法 🔥 问题根源分析 错误现象: • 导入时报错包含 data:image/SVG;base64 和 %20 等 URL 编码字符 • 代码被错误转换为 Base64 格式(适用于网页嵌入,但…

免费干净!付费软件的平替款!

今天给大家分享一款超棒的电脑录屏软件&#xff0c;简直不要太好用&#xff01;它的界面特别干净&#xff0c;没有一点儿广告&#xff0c;看起来特别清爽。 电脑录屏 无广告的录屏软件 这个软件超方便&#xff0c;根本不用安装&#xff0c;打开就能直接用。 它功能也很强大&am…

【XCP实战】AUTOSAR架构下XCP从0到1开发配置实践

目录 前言 正文 1.CAN功能开发 1.1 DBC的制作及导入 1.2 CanTrcv模块配置 1.3 Can Controller模块配置 1.4 CanIf模块配置 2.XCP模块集成配置配置 2.1.XCP模块配置 2.2.XCP模块的Task Mapping 2.3.XCP模块的初始化 3.在链接文件中定义标定段 4.编写标定相关的测试…

Vitis: 使用自定义IP时 Makefile错误 导致编译报错

参考文章: 【小梅哥FPGA】 Vitis开发中自定义IP的Makefile路径问题解决方案 Vitis IDE自定义IP Makefile错误&#xff08;arm-xilinx-eabi-gcc.exe: error: *.c: Invalid argument&#xff09;解决方法 Vitis 使用自定义IP时: Makefile 文件里的语句是需要修改的&#xff0c;…

Python中NumPy的统计运算

在数据分析和科学计算领域&#xff0c;Python凭借其丰富的库生态系统成为首选工具之一&#xff0c;而NumPy作为Python数值计算的核心库&#xff0c;凭借其高效的数组操作和强大的统计运算功能&#xff0c;广泛应用于机器学习、信号处理、统计分析等场景。本文将系统介绍NumPy在…

C语言程序环境和预处理详解

本章重点&#xff1a; 程序的翻译环境 程序的执行环境 详解&#xff1a;C语言程序的编译链接 预定义符号介绍 预处理指令 #define 宏和函数的对比 预处理操作符#和##的介绍 命令定义 预处理指令 #include 预处理指令 #undef 条件编译 程序的翻译环境和执行环…

智能工厂调度系统设计方案研究报告

一、系统架构设计 1.1 物理部署架构 设备层&#xff1a;部署大量搭载多传感器阵列的 AGV 智能循迹车&#xff0c;这些传感器包括激光雷达、视觉相机、超声波传感器等&#xff0c;用于感知周围环境信息&#xff0c;实现自主导航与避障功能&#xff1b;在每个工序节点处设置 RF…

全新突破 | 更全面 · 更安全 · 更灵活

xFile 高可用存储网关 2.0 重磅推出&#xff0c;新增多空间隔离功能从根源上防止数据冲突&#xff0c;保障各业务数据的安全性与独立性。同时支持 NFS、CIFS、FTP 等多种主流文件协议&#xff0c;无需繁琐的数据拷贝转换&#xff0c;即可与现有系统无缝对接&#xff0c;降低集成…