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.pswp.cn/news/901336.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/901336.shtml
英文地址,請注明出處:http://en.pswp.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;降低集成…