name: Filters

sort: 5

Filters

Beego supports custom filter middlewares. E.g.: user authentication and force redirection.

Activating Filters

Before filters can be used, filters must be activated.

Filters can be activated at the code level:

web.BConfig.WebConfig.Session.SessionOn = true

Filters can also be activated in the configuration file:

SessionOn = true

Attempting to use a filter without activation will cause a Handler crashed with error runtime error: invalid memory address or nil pointer dereference error

Inserting Filters

A filter function can be inserted as follows:

  1. web.InsertFilter(pattern string, pos int, filter FilterFunc, opts ...FilterOpt)

This is the FilterFunc signature:

  1. type FilterFunc func(*context.Context)

The context must be imported if this has not already been done:

  1. import "github.com/beego/beego/v2/server/web/context"

InsertFilter’s four parameters:

  • pattern: string or regex to match against router rules. Use /* to match all.
  • pos: the place to execute the Filter. There are five fixed parameters representing different execution processes.
    • web.BeforeStatic: Before finding the static file.
      • web.BeforeRouter: Before finding router.
    • web.BeforeExec: After finding router and before executing the matched Controller.
    • web.AfterExec: After executing Controller.
    • web.FinishRouter: After finishing router.
  • filter: filter function type FilterFunc func(*context.Context)
  • opts:
    1. web.WithReturnOnOutput: whether to continue running if has output. default is false.
    2. web.WithResetParams: whether to reset parameters to their previous values after the filter has completed.
    3. web.WithCaseSensitive: whether case sensitive

from beego version 1.3 AddFilter has been removed

Here is an example to authenticate if the user is logged in for all requests:

  1. var FilterUser = func(ctx *context.Context) {
  2. if strings.HasPrefix(ctx.Input.URL(), "/login") {
  3. return
  4. }
  5. _, ok := ctx.Input.Session("uid").(int)
  6. if !ok {
  7. ctx.Redirect(302, "/login")
  8. }
  9. }
  10. web.InsertFilter("/*", web.BeforeRouter, FilterUser)

Filters which use session must be executed after BeforeRouter because session is not initialized before that. web session module must be enabled first. (see Session control)

Filters can be run against requests which use a regex router rule for matching:

  1. var FilterUser = func(ctx *context.Context) {
  2. _, ok := ctx.Input.Session("uid").(int)
  3. if !ok {
  4. ctx.Redirect(302, "/login")
  5. }
  6. }
  7. web.InsertFilter("/user/:id([0-9]+)", web.BeforeRouter, FilterUser)

Filter Implementation UrlManager

Context.Input has new features RunController and RunMethod from beego version 1.1.2. These can control the router in the filter and skip the Beego router rule.

For example:

  1. var UrlManager = func(ctx *context.Context) {
  2. // read urlMapping data from database
  3. urlMapping := model.GetUrlMapping()
  4. for baseurl,rule := range urlMapping {
  5. if baseurl == ctx.Request.RequestURI {
  6. ctx.Input.RunController = rule.controller
  7. ctx.Input.RunMethod = rule.method
  8. break
  9. }
  10. }
  11. }
  12. web.InsertFilter("/*", web.BeforeRouter, UrlManager)

Filter和FilterChain

In v1.x, we can’t invoke next Filter inside a Filter. So we got a problem: we could not do something “surrounding” request execution.

For example, if we want to do:

  1. func filter() {
  2. // do something before serving request
  3. handleRequest()
  4. // do something after serving request
  5. }

The typical cases are tracing and metrics.

So we enhance Filter by designing a new interface:

  1. type FilterChain func(next FilterFunc) FilterFunc

Here is a simple example:

  1. package main
  2. import (
  3. "github.com/beego/beego/v2/core/logs"
  4. "github.com/beego/beego/v2/server/web"
  5. "github.com/beego/beego/v2/server/web/context"
  6. )
  7. func main() {
  8. web.InsertFilterChain("/*", func(next web.FilterFunc) web.FilterFunc {
  9. return func(ctx *context.Context) {
  10. // do something
  11. logs.Info("hello")
  12. // don't forget this
  13. next(ctx)
  14. // do something
  15. }
  16. })
  17. }

In this example, we only output “hello” and then we invoke next filter.

Prometheus例子

  1. package main
  2. import (
  3. "time"
  4. "github.com/beego/beego/v2/server/web"
  5. "github.com/beego/beego/v2/server/web/filter/prometheus"
  6. )
  7. func main() {
  8. // we start admin service
  9. // Prometheus will fetch metrics data from admin service's port
  10. web.BConfig.Listen.EnableAdmin = true
  11. web.BConfig.AppName = "my app"
  12. ctrl := &MainController{}
  13. web.Router("/hello", ctrl, "get:Hello")
  14. fb := &prometheus.FilterChainBuilder{}
  15. web.InsertFilterChain("/*", fb.FilterChain)
  16. web.Run(":8080")
  17. // after you start the server
  18. // and GET http://localhost:8080/hello
  19. // access http://localhost:8088/metrics
  20. // you can see something looks like:
  21. // http_request_web_sum{appname="my app",duration="1002",env="prod",method="GET",pattern="/hello",server="webServer:1.12.1",status="200"} 1002
  22. // http_request_web_count{appname="my app",duration="1002",env="prod",method="GET",pattern="/hello",server="webServer:1.12.1",status="200"} 1
  23. // http_request_web_sum{appname="my app",duration="1004",env="prod",method="GET",pattern="/hello",server="webServer:1.12.1",status="200"} 1004
  24. // http_request_web_count{appname="my app",duration="1004",env="prod",method="GET",pattern="/hello",server="webServer:1.12.1",status="200"} 1
  25. }
  26. type MainController struct {
  27. web.Controller
  28. }
  29. func (ctrl *MainController) Hello() {
  30. time.Sleep(time.Second)
  31. ctrl.Ctx.ResponseWriter.Write([]byte("Hello, world"))
  32. }

If you don’t use Beego’s admin service, don’t forget to expose prometheus‘s port.

Opentracing例子

  1. package main
  2. import (
  3. "time"
  4. "github.com/beego/beego/v2/server/web"
  5. "github.com/beego/beego/v2/server/web/filter/opentracing"
  6. )
  7. func main() {
  8. // don't forget this to inject the opentracing API's implementation
  9. // opentracing2.SetGlobalTracer()
  10. web.BConfig.AppName = "my app"
  11. ctrl := &MainController{}
  12. web.Router("/hello", ctrl, "get:Hello")
  13. fb := &opentracing.FilterChainBuilder{}
  14. web.InsertFilterChain("/*", fb.FilterChain)
  15. web.Run(":8080")
  16. // after you start the server
  17. }
  18. type MainController struct {
  19. web.Controller
  20. }
  21. func (ctrl *MainController) Hello() {
  22. time.Sleep(time.Second)
  23. ctrl.Ctx.ResponseWriter.Write([]byte("Hello, world"))
  24. }

Don’t forget to using SetGlobalTracer to initialize opentracing.