iris的middleware

middleware

在上篇文章介绍routing时有提到Party时有传入一个handler不知道读者们有没有注意到,所以今天来介绍那个middleware

本文同步放置於此

middleware

其实middleware在很多网页程序里都有,不过在介绍iris的middleware之前还是介绍一下什麽是middleware,所以接下来的内容就先介绍什麽是middleware,再来再开始介绍在iris怎麽撰写middleware。

甚麽是middleware

什麽是middleware呢,这里还是先看看middleware wiki的解释吧,对於笔者的解释与其类似,简单来说明就是在网页程序的生命周期中切入一段服务,藉此来处理所有的请求,接下来就开始跟大家介绍如何撰写iris的middleware。

irir如何撰写middleware

这边利用iris-go custom-middleware来说明如何撰写middleware,请大家先看看以下例子

func Logger() iris.Handler {
    return func(ctx iris.Context) {
        t := time.Now()

        // Set a shared variable between handlers
        ctx.Values().Set("framework", "iris")

        // before request

        ctx.Next()

        // after request
        latency := time.Since(t)
        log.Print(latency)

        // access the status we are sending
        status := ctx.GetStatusCode()
        log.Println(status)
    }
}

func main() {
    app := iris.New()
    app.Use(Logger())

    app.Get("/test", func(ctx iris.Context) {
        // retrieve a value set by the middleware.
        framework := ctx.Values().GetString("framework")

        // it would print: "iris"
        log.Println(framework)
    })

    app.Listen(":8080")
}

简单讲如何撰写一个自订义的middleware就是返还一个handdler,撰写middleware的要点主要是区分处理请求之前跟处理完请求的部分,看到上面的例子就知道其中的区隔是一个ctx.Next(),呼叫这个方法之前是处理请求之前,呼叫完就是处理请求之後。
然而如何分享变数在handler之间呢,就是透过ctx.Values().Set(key string, value object)ctx.Values().GetString(key string) string之类的方法。

如何使用middleware

上面例子说明如何撰写middleware,也提到一个如何使用middleware的方法,简单来说就是透过app.Use(h handler)来使用middleware,不过使用的方式不只这样,还有下列几种方法

  • Party.UseError
  • Party.Use
  • Party.UseOnce
  • Application.UseGlobal
  • Party.Done
  • Application.DoneGlobal
  • Party.UseRouter
  • Application.WrapRouter

至於其中的差别可以看看iris-go middleware的内容,首先先看看他的例子

package main

import (
    "net/http"

    "github.com/kataras/iris/v12"
)

func main() {
    app := iris.New()

    app.WrapRouter(routerWrapper)
    app.UseRouter(routerMiddleware)
    app.UseGlobal(globalMiddleware)
    app.Use(useMiddleware)
    app.UseError(errorMiddleware)
    // app.Done(done)
    // app.DoneGlobal(doneGlobal)

    // Adding a OnErrorCode(iris.StatusNotFound) causes `.UseGlobal`
    // to be fired on 404 pages without this,
    // only `UseError` will be called, and thus should
    // be used for error pages.
    app.OnErrorCode(iris.StatusNotFound, notFoundHandler)

    app.Get("/", mainHandler)

    app.Listen(":8080")
}

func mainHandler(ctx iris.Context) {
    ctx.WriteString("Main Handler")
}

func notFoundHandler(ctx iris.Context) {
    ctx.WriteString("404 Error Handler")
}

上面是主程序以及绑定middleware的资讯,接下来看看期middleware的内容

func routerWrapper(w http.ResponseWriter, r *http.Request,
    router http.HandlerFunc) {

    if r.URL.Path == "/" {
        w.Write([]byte("#1 .WrapRouter\n"))
        /* Note for new Gophers:
            If we Write anything here on an error resource in the raw
            `net/http` wrapper like this one, then the response writer will
            automatically send a `200` OK status code (when we first write).
            Any error handler executed after this will not fire as expected.
            Also, when `w.WriteHeader` is called you can NOT change the
            status code later on.

            In Iris Handlers, if you write before the status code has been
            set, then it will also automatically send the 200 OK status
            code which then cannot be changed later. However, if we call
            `ctx.StatusCode` inside an Iris Handler without writing any
            content, then we can change the status code later on. When you
            need to change that behaviour, you must start the handler with
            a `ctx.Record` call.
        */
    }

    // Continue by executing the Iris Router and let it do its job.
    router(w, r)
}


func routerMiddleware(ctx iris.Context) {
    if ctx.Path() == "/" {
        ctx.WriteString("#2 .UseRouter\n")
    // The same caveat described in routerWrapper applies here as well.
    }

    ctx.Next()
}

func globalMiddleware(ctx iris.Context) {
    ctx.WriteString("#3 .UseGlobal\n")
    ctx.Next()
}

func useMiddleware(ctx iris.Context) {
    ctx.WriteString("#4 .Use\n")
    ctx.Next()
}

func errorMiddleware(ctx iris.Context) {
    ctx.WriteString("#3 .UseError\n")
    ctx.Next()
}

这里的middleware都只是写log而已,然後执行这个程序会有甚麽结果呢,请大家看看以下的内容

#1 .WrapRouter
#2 .UseRouter
#3 .UseGlobal
#4 .Use
Main Handler

由上面例子可以知道,注册middleware的执行顺序是如上面显示的顺序WrapRouter最先被执行、Use最後,接下来才执行绑定的handler,除此之外如果触发404会有下列的输出

#3 .UseGlobal
#3 .UseError
404 Error Handler

不过这里有一个部分需要注意的,就是如果OnErrorCode没有绑定到的状态被触发,UseGlobal也不会被触发,例如触发的不是404而是400则会输出的log如下

#3 .UseError
Not Found

修改middleware

最後跟大家说明如何修改原来的middleware,简单说就是像接水管一样,再接原来的middleware之前先接另外一个middleware去判断後即可,大家可以看看下列例子

package main

import (
    "github.com/kataras/iris/v12"
    "github.com/kataras/iris/v12/middleware/basicauth"
)

func main() {
    users := map[string]string{"username":"password"}
    auth := basicauth.New(basicauth.Default(users))

    app.UseRouter(skipStaticSubdomain(auth)) // <--

    // [...]
    app.Listen(":80")
}

func skipStaticSubdomain(handler iris.Handler) iris.Handler {
    return func(ctx iris.Context) {
        if ctx.Subdomain() == "static." {
            // continue to the next or main handler and exit.
            ctx.Next()
            return
        }

        handler(ctx)   
    }
}

除了上述的例子之外,另外可以把conditionhandler分开来写,透过iris.NewConditionalHandler(filter Filter, handlers ...Handler) Handler来改写middleware,详见下列例子

app.UseRouter(iris.NewConditionalHandler(isNotStaticSubdomain, auth))

func isNotStaticSubdomain(ctx iris.Context) bool {
    return ctx.Subdomain() != "static."
}

结论

本篇介绍什麽是middleware,并且介绍如何在iris撰写middleware,希望大家对於iris的middleware有初步的概念。


<<:  【Day 23】React 关於 Hook(3)

>>:  Day 28 讨论 AI 深度学习论点

【Day 18】Shellcode 与他的快乐夥伴 (上) - Shellcode Loader

环境 Windows 10 21H1 Visual Studio 2019 前情提要 在【Day 0...

什麽是功能分解?

功能分解对应於各种功能关系,如原始复杂业务功能的开发方式。它主要关注如何开发整体功能及其各个组件之间...

[Day25]DDL语句建立资料表2

建立资料表:CREATE TABLE 说明: 建立资料表之前,首先必须拥有DBA授权的CREATE ...

Day 2— 你的第一次闻GAS

今天来直接上手开始我们的 Google Apps Script 之旅啦! 我们在使用 Google ...

D7 allauth 采坑日记 Extending & Substituting User model (2)

接续上一篇 这次要讲的是我研究中途试过的另一个方法 Substituting 这其实是我一开始的想法...