Go 微服务框架 | 中间件

文章目录

定义中间件

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

前置中间件

go 复制代码
// zj.go

package zjgo

import (
	"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] = handleFunc
		routerGroup.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}
			// 先判断当前请求路由是否支持任意请求方式的Any
			if 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)
	}
}
go 复制代码
// main.go

package main

import (
	"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 handler
	g1.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()

}

后置中间件

go 复制代码
// zj.go

package zjgo

import (
	"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] = handleFunc
		routerGroup.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}
			// 先判断当前请求路由是否支持任意请求方式的Any
			if 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)
	}
}
go 复制代码
// main.go

package main

import (
	"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 handler
	g1.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()

}
  • 但是这里前置和后置中间件似乎有点多余了,因为在前置中间件中,执行完一系列前置中间件和主体业务函数后,就可以执行后置中间件了,不用其他冗余代码。
go 复制代码
// zj.go

package zjgo

import (
	"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] = handleFunc
		routerGroup.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}
			// 先判断当前请求路由是否支持任意请求方式的Any
			if 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)
	}
}
go 复制代码
// main.go

package main

import (
	"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 handler
	g1.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()

}

路由级别中间件

go 复制代码
// zj.go

package zjgo

import (
	"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] = handleFunc
		routerGroup.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}
			// 先判断当前请求路由是否支持任意请求方式的Any
			if 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)
	}
}
go 复制代码
// main.go
package main

import (
	"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 handler
	g1.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
相关推荐
二狗哈1 小时前
go游戏后端开发34:补杠功能与到时出牌
数据库·游戏·golang
爱的叹息1 小时前
spring cloud微服务API网关详解及各种解决方案详解
spring·spring cloud·微服务
余瑾瑜1 小时前
宝塔面板安装MySQL数据库并通过内网穿透工具实现公网远程访问
开发语言·后端·golang
万能的编程语言6 小时前
微服务之间调用外键“翻译”的方法概述
微服务·id翻译name
顾云澜6 小时前
Apache Superset本地部署结合内网穿透实现无公网IP远程查看数据
开发语言·后端·golang
北极象7 小时前
使用Golang打包jar应用
python·golang·jar
爱的叹息7 小时前
关于 微服务负载均衡 的详细说明,涵盖主流框架/解决方案的对比、核心功能、配置示例及总结表格
微服务·架构·负载均衡
二狗哈7 小时前
go游戏后端开发29:实现游戏内聊天
服务器·游戏·golang
forestsea7 小时前
微服务面试题:服务网关和链路追踪
微服务·云原生·架构
极客先躯8 小时前
高级java每日一道面试题-2025年3月31日-微服务篇[Nacos篇]-Nacos集群模式下的部署方案有哪些?
java·开发语言·微服务