Go Fiber 框架系列:中間件
大家好,我是 polarisxu。
Middleware(中間件) 是一個(gè) Web 框架重要的組成部分,通過這種模式,可以方便的擴(kuò)展框架的功能。目前 Go Web 框架都提供了 Middleware 的功能,也有眾多可用的 Middleware。
Fiber 也是如此,官方提供了眾多的 Middleware,方便用戶直接使用。本文先看看 Fiber 中 Middleware 的定義,然后介紹 Fiber 中的幾個(gè) Middleware,最后自己實(shí)現(xiàn)一個(gè) Middleware。
Fiber 文檔中關(guān)于 Middleware 的說明:中間件是在 HTTP 請求周期中鏈接的函數(shù),它可以訪問用于執(zhí)行特定操作(例如,記錄每個(gè)請求或啟用 CORS)的上下文。
01 Middleware 長什么樣
設(shè)計(jì)用于更改請求或響應(yīng)的函數(shù)稱為中間件函數(shù)。Next 是 Fiber 路由器函數(shù),當(dāng)它被調(diào)用時(shí),執(zhí)行與當(dāng)前路由匹配的下一個(gè)函數(shù)。
可見,中間件其實(shí)和 Handler 是一樣的,只是用途有區(qū)別?;蛘哒f至少簽名是一樣的,這樣才能更好的形成一個(gè)鏈。
因此,F(xiàn)iber 中的中間件簽名如下:
- func(ctx *fiber.Ctx) error
Fiber 沒有專門定義中間件類型。
此外,從 fiber.App.Use 方法也可以看到,中間件和普通的 Handler 并無本質(zhì)不同。
- // Use registers a middleware route that will match requests
- // with the provided prefix (which is optional and defaults to "/").
- //
- // app.Use(func(c *fiber.Ctx) error {
- // return c.Next()
- // })
- // app.Use("/api", func(c *fiber.Ctx) error {
- // return c.Next()
- // })
- // app.Use("/api", handler, func(c *fiber.Ctx) error {
- // return c.Next()
- // })
- //
- // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
- func (app *App) Use(args ...interface{}) Router {
- var prefix string
- var handlers []Handler
- for i := 0; i < len(args); i++ {
- switch arg := args[i].(type) {
- case string:
- prefix = arg
- case Handler:
- handlers = append(handlers, arg)
- default:
- panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg)))
- }
- }
- app.register(methodUse, prefix, handlers...)
- return app
- }
而 fiber.Handler 類型只是 func(*fiber.Ctx) error 的別名:
- // Handler defines a function to serve HTTP requests.
- type Handler = func(*Ctx) error
這點(diǎn)上,Gin 框架和 Fiber 是類似的。不過,有一些框架,比如 Echo,專門定義了中間件類型。但不管怎么樣,中間件的本質(zhì)和普通路由 Handler 是類似的。
02 Fiber 內(nèi)置的中間件
所有內(nèi)置的中間件可以在 fiber 項(xiàng)目的 middleware 子包找到,這些中間件對應(yīng)的文檔在這里:https://docs.gofiber.io/api/middleware。
以 Recover 中間件為例,看看官方中間件的實(shí)現(xiàn)方法,我們自己的中間件可以參照實(shí)現(xiàn)。
1)簽名
- func New(config ...Config) fiber.Handler
上文說了,中間件就是一個(gè) Handler,因此這里 New 函數(shù)返回 fiber.Handler,這就中間件。
至于 New 函數(shù)的參數(shù)不做任何要求,只需要最終返回 fiber.Handler 即可。
2)配置
一個(gè)好的中間件,或通用的中間件,一般都會有配置,讓中間件更靈活、更強(qiáng)大??纯?Recover 的配置定義:
- // Config defines the config for middleware.
- type Config struct {
- // Next defines a function to skip this middleware when returned true.
- //
- // Optional. Default: nil
- Next func(c *fiber.Ctx) bool
- // EnableStackTrace enables handling stack trace
- //
- // Optional. Default: false
- EnableStackTrace bool
- // StackTraceHandler defines a function to handle stack trace
- //
- // Optional. Default: defaultStackTraceHandler
- StackTraceHandler func(e interface{})
- }
具體配置是什么樣的,需要根據(jù)中間件的功能來定義。
不過,配置中 Next 這個(gè)行為,很多中間件都可以有。
3)默認(rèn)配置
一般的,會提供一個(gè)默認(rèn)配置,方便使用。而且,大部分時(shí)候,使用默認(rèn)配置即可。Recover 的默認(rèn)配置如下:
- var ConfigDefault = Config{
- Next: nil,
- EnableStackTrace: false,
- StackTraceHandler: defaultStackTraceHandler,
- }
如果這樣調(diào)用 recover.New() ,會默認(rèn)使用上面的默認(rèn)配置。
最后看看 New 函數(shù)的代碼:
- // New creates a new middleware handler
- func New(config ...Config) fiber.Handler {
- // Set default config
- cfg := configDefault(config...)
- // Return new handler
- return func(c *fiber.Ctx) (err error) {
- // Don't execute middleware if Next returns true
- if cfg.Next != nil && cfg.Next(c) {
- return c.Next()
- }
- // Catch panics
- defer func() {
- if r := recover(); r != nil {
- if cfg.EnableStackTrace {
- cfg.StackTraceHandler(r)
- }
- var ok bool
- if err, ok = r.(error); !ok {
- // Set error that will call the global error handler
- err = fmt.Errorf("%v", r)
- }
- }
- }()
- // Return err if exist, else move to next handler
- return c.Next()
- }
- }
以上就是一個(gè) Fiber 標(biāo)準(zhǔn)中間件的寫法。
具體使用時(shí)就是:app.Use(recover.New())。
當(dāng)然,如果只是自己項(xiàng)目使用,可以不用寫配置。
03 實(shí)現(xiàn)一個(gè)簡單的中間件
通過 Recover 學(xué)習(xí)到了中間件的標(biāo)準(zhǔn)寫法,如果中間件只在自己項(xiàng)目使用,不需要靈活性,完全可以采用簡單的寫法。
- func Security(ctx *fiber.Ctx) error {
- ctx.Set("X-XSS-Protection", "1; mode=block")
- ctx.Set("X-Content-Type-Options", "nosniff")
- ctx.Set("X-Download-Options", "noopen")
- ctx.Set("Strict-Transport-Security", "max-age=5184000")
- ctx.Set("X-Frame-Options", "SAMEORIGIN")
- ctx.Set("X-DNS-Prefetch-Control", "off")
- // 執(zhí)行下一個(gè) Handler
- return ctx.Next()
- }
這其實(shí)也是一個(gè) Handler,對吧。無非最后調(diào)用的是 ctx.Next,而不是 ctx.JSON 之類的。
使用時(shí)這樣:
- app := fiber.New()
- app.Use(Security)
只要理解中間件的機(jī)制,不需要拘泥于具體形式,可以靈活變換中間件的寫法。
04 總結(jié)
本文講解了什么是中間件,F(xiàn)iber 中間件長什么樣以及對內(nèi)置中間件 Recover 的學(xué)習(xí),最后自己實(shí)現(xiàn)一個(gè)簡單的中間件。掌握了 Fiber 的中間件,相信對其他 Go Web 框架的中間件的學(xué)習(xí)也就不難了,因?yàn)槎疾畈欢唷?/p>