手把手和你一起實現一個Web框架實戰——EzWeb框架(五)[Go語言筆記]Go專案實戰
程式碼倉庫:
github
gitee
中文註釋,非常詳盡,可以配合食用
本篇程式碼,請選擇demo5
中介軟體實現
一、Context設計
type Context struct {
Writer http.ResponseWriter
Req *http.Request
//請求的資訊,包括路由和方法
Path string
Method string
Params map[string]string /*用於儲存外面拿到的引數 ":xxx" or "*xxx" */
//響應的狀態碼
StatusCode int
//中介軟體
handlers []HandlerFunc
index int /* 用於記錄當前執行到第幾個中介軟體 */
}
我們每次請求生成的context,我們選擇在其中放入和我們中介軟體和執行控制變數index
二、中介軟體對路由組的註冊方法
// 將路中介軟體,放入路由組的中介軟體方法切片中
func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
group.middlewares = append(group.middlewares, middlewares...)
}
當每個請求到來後,ServeHTTP函式執行時,它將該生成一個context並將進行傳入的url和路由組字首做字首對比,找到滿足條件的路由組,取出它的中介軟體,然後存入到生成的context。
// ServeHTTP 方法的實現,用於實現處理HTTP請求
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var middlewares []HandlerFunc
for _, group := range engine.groups {
//比對路由組存的字首和請求路徑,把屬於這個請求對映的路由組中的中介軟體取到
//意思就是比對發現該請求屬於哪一個路由組,需要哪些中介軟體,取出來執行
if strings.HasPrefix(req.URL.Path, group.prefix) {
middlewares = append(middlewares, group.middlewares...)
}
}
//根據req和w例項一個context
c := newContext(w, req)
//將取道的中介軟體賦給這個context
c.handlers = middlewares
//通過封裝好的context執行處理
engine.router.handle(c)
}
三、處理函式
在處理函式handle()中,我們根據路由拿到已經註冊的方法,放入到中介軟體後,在通過Next函式進行處理
//根據context中儲存的 c.Method 和 c.Path 拿到對應的處理方法,進行執行,如果拿到的路由沒有註冊,則返回404
func (r *router) handle(c *Context) {
//獲取匹配到的節點,同時也拿到兩類動態路由中引數
n, params := r.getRoute(c.Method, c.Path)
if n != nil {
c.Params = params
//拿目的節點中的path做key來找handlers
key := c.Method + "-" + n.path
//根據路徑拿到處理器
c.handlers = append(c.handlers, r.handlers[key])
}else {
//不存在節點的情況下,給生成的c加入一個404方法
c.handlers = append(c.handlers, func(c *Context) {
c.String(http.StatusNotFound, "404 NOT FOUND: ", c.Path)
})
}
c.Next()
}
四、Next方法執行處理
index進行控制,遍歷完c.handlers中儲存的方法執行。
// 當中介軟體呼叫了 Next 方法時,就往後執行下一個,同時index交由下一個中介軟體控制
func (c *Context) Next() {
c.index++
//執行完之後所有的handlers
for ;c.index < len(c.handlers); c.index++{
c.handlers[c.index](c)
}
}
這裡的日誌中介軟體在Next中被執行時,也呼叫了Next函式。這裡第四行的執行巧妙的做到了,控制index++,執行下一個中介軟體,直到執行完了又回到Logger執行,回到原來的Next的for迴圈,發現不滿足繼續迴圈的條件,然後退出。
func Logger() HandlerFunc {
return func(c *Context) {
t := time.Now()
c.Next()
log.Printf("[%d] %s in %v", c.StatusCode, c.Req.RequestURI, time.Since(t))
}
}
demo測試:
/*
@Time : 2021/8/16 下午4:01
@Author : mrxuexi
@File : main
@Software: GoLand
*/
package main
import (
"Ez"
"net/http"
)
func main() {
r := Ez.New()
//給所有的路由組都新增了中介軟體logger
r.Use(Ez.Logger())
api := r.Group("/api")
api.POST("/hello", func(c *Ez.Context) {
c.JSON(http.StatusOK,Ez.H{
"message" : "hello",
})
})
//next的應用
api.Use(func(c *Ez.Context) {
c.JSON(200,Ez.H{
"test" : "middleware2-1",
})
c.Next()
c.JSON(200, Ez.H{
"test" : "middleware2-2",
})
})
r.Run(":9090")
}
成功!
參考:
[1]: https://github.com/geektutu/7days-golang/tree/master/gee-web ""gee""
[2]: https://github.com/gin-gonic/gin ""gin""