Go Revel - Filter(过滤器)源码分析

在 Go Revel - server.go 源码分析 http://www.cnblogs.com/hangxin1940/p/3265538.html

说到revel框架很多重要的东西都Filters过滤器链中处理。

Ftilers链是整个revel框架的处理核心。

Filters(过滤器链)

golang_filters

filter.go

package revel

type Filter func(c *Controller, filterChain []Filter)

// Filters 为全局的过滤器链
// 项目初始化时Filters就会被设置
var Filters = []Filter{
    PanicFilter,             // 运行时异常过滤器    恢复一个 panics 运行时异常并且显示异常信息页面
    RouterFilter,            // 路由过滤器       根据路由器选择正确的Action
    FilterConfiguringFilter, // 自定义过滤器配置器       为每一个Action增加或删除自定义过滤器
    ParamsFilter,            // 参数转换过滤器 将请求的参数转换为 Controller.Params
    SessionFilter,           // 会话过滤器       恢复和写入会话cookie
    FlashFilter,             // Flash过滤器        恢复和写入Flash信息cookie
    ValidationFilter,        // 验证过滤器       恢复保存验证错误并且从cookie中新建一个
    I18nFilter,              // i18n过滤器     解析请求的语言
    InterceptorFilter,       // 拦截器过滤器      在Action前后运行拦截器
    ActionInvoker,           // Action过滤器       调用Action方法
}

// NilFilter and NilChain are helpful in writing filter tests.
var (
    NilFilter = func(_ *Controller, _ []Filter) {}
    NilChain  = []Filter{NilFilter}
)

符合func(c *Controller, filterChain []Filter)这两种参数形式的函数,均可以当作过滤器。

Filter是一个固定参数的方法,并且内部方法实现为级联递归调用。每次掉用,会传入controller以及当前Filters长度-1的一个切片,在方法最后会递归调用下去,直到传入的Filters切片没有元素。

通过filterconfig可以很方便的添加自定义过滤器,如源码中注释的示例:

// FilterConfigurator allows the developer configure the filter chain on a
// per-controller or per-action basis.  The filter configuration is applied by
// the FilterConfiguringFilter, which is itself a filter stage.  For example,
//
// Assuming:
//   Filters = []Filter{
//     RouterFilter,
//     FilterConfiguringFilter,
//     SessionFilter,
//     ActionInvoker,
//   }
//
// Add:
//   FilterAction(App.Action).
//     Add(OtherFilter)
//
//   => RouterFilter, FilterConfiguringFilter, SessionFilter, OtherFilter, ActionInvoker
//
// Remove:
//   FilterAction(App.Action).
//     Remove(SessionFilter)
//
//   => RouterFilter, FilterConfiguringFilter, OtherFilter, ActionInvoker
//
// Insert:
//   FilterAction(App.Action).
//     Insert(OtherFilter, revel.BEFORE, SessionFilter)
//
//   => RouterFilter, FilterConfiguringFilter, OtherFilter, SessionFilter, ActionInvoker
//
// Filter modifications may be combined between Controller and Action.  For example:
//   FilterController(App{}).
//     Add(Filter1)
//   FilterAction(App.Action).
//     Add(Filter2)
//
//  .. would result in App.Action being filtered by both Filter1 and Filter2.
//
// Note: the last filter stage is not subject to the configurator.  In
// particular, Add() adds a filter to the second-to-last place.

只需要对要过滤的ControllerAction应用到FilterControllerFilterAction函数,并Add添加自定义过滤器就行了。

Filters的调用方式决定了过滤器链的调用是有序的,在默认的Filters切片中,调用的顺序既是其元素定义的顺序。

1. PanicFilter(处理运行时异常)

PanicFilter用来处理所有运行时异常信息,它使用recover()来捕获所有运行时异常。它被安排为第一个过滤器是有原因的。

定义如下:

// PanicFilter wraps the action invocation in a protective defer blanket that
// converts panics into 500 error pages.
func PanicFilter(c *Controller, fc []Filter) {
    defer func() {
        if err := recover(); err != nil {
            handleInvocationPanic(c, err)
        }
    }()
    fc[0](c, fc[1:])
}

// This function handles a panic in an action invocation.
// It cleans up the stack trace, logs it, and displays an error page.
func handleInvocationPanic(c *Controller, err interface{}) {
    error := NewErrorFromPanic(err)
    if error == nil {
        ERROR.Print(err, "\n", string(debug.Stack()))
        c.Response.Out.WriteHeader(500)
        c.Response.Out.Write(debug.Stack())
        return
    }

    ERROR.Print(err, "\n", error.Stack)
    c.Result = c.RenderError(error)
}

首先会定一个defer闭包,之后继续调用过滤器链。

这很像一个递归,确保了它以后的所有过滤器链执行完后这个defer闭包才会执行。而PanicFilter又是第一个被执行的过滤器,这样在运行recover()时,能确保它会捕获所有异常。

2. RouterFilter(处理路由)

项目中conf/routes配置的路由信息,将在这里被正确的转到各个Controller/Action

func RouterFilter(c *Controller, fc []Filter) {
    // Figure out the Controller/Action
    var route *RouteMatch = MainRouter.Route(c.Request.Request)
    if route == nil {
        c.Result = c.NotFound("No matching route found")
        return
    }

    // The route may want to explicitly return a 404.
    if route.Action == "404" {
        c.Result = c.NotFound("(intentionally)")
        return
    }

    // Set the action.
    if err := c.SetAction(route.ControllerName, route.MethodName); err != nil {
        c.Result = c.NotFound(err.Error())
        return
    }

    // Add the route and fixed params to the Request Params.
    c.Params.Route = route.Params

    // Add the fixed parameters mapped by name.
    // TODO: Pre-calculate this mapping.
    for i, value := range route.FixedParams {
        if c.Params.Fixed == nil {
            c.Params.Fixed = make(url.Values)
        }
        if i < len(c.MethodType.Args) {
            arg := c.MethodType.Args[i]
            c.Params.Fixed.Set(arg.Name, value)
        } else {
            WARN.Println("Too many parameters to", route.Action, "trying to add", value)
            break
        }
    }

    fc[0](c, fc[1:])
}

首先,通过检查请求信息,生成RouteMatch对象,它包含了一个路由所对应的信息。 然后为Controller参数设置正确的请求信息,如实例化一个目标ControllerParams请求参数等,如果未找到正确路由则设置Result,并直接返回,终止过滤器链的调用。

3. FilterConfiguringFilter(处理自定义过滤器)

前面讲过自定义过滤器的实现,这里则是如何调用它们

// FilterConfiguringFilter is a filter stage that customizes the remaining
// filter chain for the action being invoked.
func FilterConfiguringFilter(c *Controller, fc []Filter) {
    if newChain := getOverrideChain(c.Name, c.Action); newChain != nil {
        newChain[0](c, newChain[1:])
        return
    }
    fc[0](c, fc[1:])
}

// getOverrideChain retrieves the overrides for the action that is set
func getOverrideChain(controllerName, action string) []Filter {
    if newChain, ok := filterOverrides[action]; ok {
        return newChain
    }
    if newChain, ok := filterOverrides[controllerName]; ok {
        return newChain
    }
    return nil
}

首先从filterOverrides这个map中匹配是否存在相应Controller/Action的过滤器,如果存在则链式调用。

自定义过滤器全部调用完后,则进入下一环节过滤器的调用。

4. ParamsFilter(处理请求表单参数)

func ParamsFilter(c *Controller, fc []Filter) {
    ParseParams(c.Params, c.Request)

    // Clean up from the request.
    defer func() {
        // Delete temp files.
        if c.Request.MultipartForm != nil {
            err := c.Request.MultipartForm.RemoveAll()
            if err != nil {
                WARN.Println("Error removing temporary files:", err)
            }
        }

        for _, tmpFile := range c.Params.tmpFiles {
            err := os.Remove(tmpFile.Name())
            if err != nil {
                WARN.Println("Could not remove upload temp file:", err)
            }
        }
    }()

    fc[0](c, fc[1:])
}

ParamsFilter把请求中的表单参数或者上传的文件正确转换为相应的类型或者临时文件保存为Controller.Params

它也使用了defer闭包,确保在过滤器链退出时把生成的临时文件全部删除。

5. SessionFilter(处理会话cookies)

func SessionFilter(c *Controller, fc []Filter) {
    c.Session = restoreSession(c.Request.Request)

    fc[0](c, fc[1:])

    // Store the session (and sign it).
    c.SetCookie(c.Session.cookie())
}

这里只是简单的从请求获取cookie,并在所有过滤器链调用完后再调用ControllerSetCookie方法将cookie发送给客户端。

在最后调用SetCookie方法,是因为SessionFilter之后的过滤器可能会对cookies进行操作。

6. FlashFilter(处理Flash cookies)

flash的生命周期很短,仅限于一次请求。

func FlashFilter(c *Controller, fc []Filter) {
    c.Flash = restoreFlash(c.Request.Request)
    c.RenderArgs["flash"] = c.Flash.Data

    fc[0](c, fc[1:])

    // Store the flash.
    var flashValue string
    for key, value := range c.Flash.Out {
        flashValue += "\x00" + key + ":" + value + "\x00"
    }
    c.SetCookie(&http.Cookie{9
        Name:  CookiePrefix + "_FLASH",
        Value: url.QueryEscape(flashValue),
        Path:  "/",
    })
}

这里会设置Controller对象Flash相关的字段,然后调用后面的过滤器链,并在最后将这个Flash 写入cookie

7. ValidationFilter(处理验证器)

这里会在每次调用controller之前为每个controller实例创建一个Validation验证器:

func ValidationFilter(c *Controller, fc []Filter) {
    errors, err := restoreValidationErrors(c.Request.Request)
    c.Validation = &Validation{
        Errors: errors,
        keep:   false,
    }
    hasCookie := (err != http.ErrNoCookie)

    fc[0](c, fc[1:])

    // Add Validation errors to RenderArgs.
    c.RenderArgs["errors"] = c.Validation.ErrorMap()

    // Store the Validation errors
    var errorsValue string
    if c.Validation.keep {
        for _, error := range c.Validation.Errors {
            if error.Message != "" {
                errorsValue += "\x00" + error.Key + ":" + error.Message + "\x00"
            }
        }
    }

    // When there are errors from Validation and Keep() has been called, store the
    // values in a cookie. If there previously was a cookie but no errors, remove
    // the cookie.
    if errorsValue != "" {
        c.SetCookie(&http.Cookie{
            Name:  CookiePrefix + "_ERRORS",
            Value: url.QueryEscape(errorsValue),
            Path:  "/",
        })
    } else if hasCookie {
        c.SetCookie(&http.Cookie{
            Name:   CookiePrefix + "_ERRORS",
            MaxAge: -1,
            Path:   "/",
        })
    }
}

// Restore Validation.Errors from a request.
func restoreValidationErrors(req *http.Request) ([]*ValidationError, error) {
    var (
        err    error
        cookie *http.Cookie
        errors = make([]*ValidationError, 0, 5)
    )
    if cookie, err = req.Cookie(CookiePrefix + "_ERRORS"); err == nil {
        ParseKeyValueCookie(cookie.Value, func(key, val string) {
            errors = append(errors, &ValidationError{
                Key:     key,
                Message: val,
            })
        })
    }
    return errors, err
}

// Register default validation keys for all calls to Controller.Validation.Func().
// Map from (package).func => (line => name of first arg to Validation func)
// E.g. "myapp/controllers.helper" or "myapp/controllers.(*Application).Action"
// This is set on initialization in the generated main.go file.
var DefaultValidationKeys map[string]map[int]string

首先,restoreValidationErrors方法尝试从客户端请求的cookie中恢复出错的验证信息。

然后为Controller实例创建一个Validation对象。

接着继续过滤器链的调用。

所有过滤器链调用完后,根据Validation.keep得值判断是否将错误的验证信息写入cookies中从而在整个绘画中保存验证信息。

通常,验证器在自定义ControllerAction中使用,根据情况可以调用Validation.keep来将错误信息保持在会话中。

DefaultValidationKeys中存储的是整个项目源码中所有调用验证的行号与名称,在main.go中为它赋值。在调用验证之后,默认会调用她的apply方法, 它会将DefaultValidationKeys存储的行号等详细信息组装为ValidationResult

关于DefaultValidationKeys的生成 具体请移步 Go Revel - main函数分析 http://www.cnblogs.com/hangxin1940/p/3263775.html

8. I18nFilter(处理国际化i18n)

判断header或cookie中是否存在语言声明字段,然后定义controllerRequest.Locale对象,方便后续工作中处理国际化i18n

9. InterceptorFilter(处理拦截器)

执行4种拦截器: BEFOREAFTERPANICFINALLY

func InterceptorFilter(c *Controller, fc []Filter) {
    defer invokeInterceptors(FINALLY, c)
    defer func() {
        if err := recover(); err != nil {
            invokeInterceptors(PANIC, c)
            panic(err)
        }
    }()

    // Invoke the BEFORE interceptors and return early, if we get a result.
    invokeInterceptors(BEFORE, c)
    if c.Result != nil {
        return
    }

    fc[0](c, fc[1:])
    invokeInterceptors(AFTER, c)
}

func invokeInterceptors(when When, c *Controller) {
    var (
        app    = reflect.ValueOf(c.AppController)
        result Result
    )
    for _, intc := range getInterceptors(when, app) {
        resultValue := intc.Invoke(app)
        if !resultValue.IsNil() {
            result = resultValue.Interface().(Result)
        }
        if when == BEFORE && result != nil {
            c.Result = result
            return
        }
    }
    if result != nil {
        c.Result = result
    }
}

第一行的defer执行FINALLY阶段的拦截器,它在方法退出前执行,第二个defer闭包则捕捉运行时异常,用以拦截处理PANIC阶段的拦截器,它会在FINALLY之前调用,此时能捕获绝大部分的运行时异常。

BEFOREAFTER拦截器分别在运行过滤器链代码的前后执行,而此时整个过滤器链已剩下最后一个过滤器没调用了。

10. ActionInvoker(由开发者处理Action请求)

到了这里,代码就交给了开发者,它会根据请求找到并执行自定义的controller中的action,这里完全有开发者做主。

func ActionInvoker(c *Controller, _ []Filter) {
    // Instantiate the method.
    methodValue := reflect.ValueOf(c.AppController).MethodByName(c.MethodType.Name)

    // Collect the values for the method's arguments.
    var methodArgs []reflect.Value
    for _, arg := range c.MethodType.Args {
        // If they accept a websocket connection, treat that arg specially.
        var boundArg reflect.Value
        if arg.Type == websocketType {
            boundArg = reflect.ValueOf(c.Request.Websocket)
        } else {
            TRACE.Println("Binding:", arg.Name, "as", arg.Type)
            boundArg = Bind(c.Params, arg.Name, arg.Type)
        }
        methodArgs = append(methodArgs, boundArg)
    }

    var resultValue reflect.Value
    if methodValue.Type().IsVariadic() {
        resultValue = methodValue.CallSlice(methodArgs)[0]
    } else {
        resultValue = methodValue.Call(methodArgs)[0]
    }
    if resultValue.Kind() == reflect.Interface && !resultValue.IsNil() {
        c.Result = resultValue.Interface().(Result)
    }
}

这里通过反射来调用之前在RouterFilter中生成的contrller实例的Action方法,并将转换过的请求参数传入,这里就真正进入自己构造的controller中了。

所有过滤器链处理完后,会返回至server.go中。 移步至 Go Revel - server.go 源码分析 http://www.cnblogs.com/hangxin1940/p/3265538.html

posted on   黑暗伯爵  阅读(3521)  评论(0编辑  收藏  举报

编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示