拦截器是一个action(处理器函数)之前或之后被调用的函数。拦截器能够用于以下常见问题:
wego中有两种形式的拦截器,一种是函数型拦截器,函数型的形式与一般的处理器函数相同。wego中有以下三种拦截器:
接下来的例子我们使用拦截器来验证用户登录状态,并使用cookie引擎来存储session数据。
func main() {
web, err := wego.NewWeb()
if err != nil{
log.Error(err)
return
}
//初始化session
store := cache.NewCookieStore("demo", "1111")
web.Session.Init(store)
//设置BeforExec拦截器
web.BeforExec(hookBefore)
//路由注册
web.GET("/login", login)
web.GET("/index", index)
err = web.Run(":8080")
if err != nil {
log.Error(err)
}
}
上面的代码中我们使用了cookie引擎来存储session数据,并注册了两个处理器函数,login以及index。login处理器的定义如下:
func login(c *wego.WebContext) {
c.Session.Set("uid", 1)
c.Session.Save()
c.Redirect(302, "/index")
}
login函数处理器用于将用户登录信息写入session,并跳转到index页面
func hookBefore(c *wego.WebContext) {
id, err := c.Session.GetInt("uid")
if err != nil || id < 0 {
c.Redirect(302, "/login")
}
c.Data.Set("uid", id)
}
hookBefore就是一个拦截器,其形式与函数型处理器是相同的。在hookBefore中我们读取session中的数据,判断是否存在uid,若不存在则跳转到登录页面:/login
func index(c *wego.WebContext) {
id , _ := c.Data.GetInt64("uid")
c.WriteTextF(200, "uid=%d", id)
}
wego中的另外一种拦截器是接口拦截器。wego中定义了以下两种接口用于拦截HTTP请求:
type BeforeExecer interface {
BeforeExec(ctx *WebContext)
}
type AfterExecer interface {
AfterExec(ctx *WebContext)
}
一个struct若实现了BeforeExecer接口,则在执行action之前会先执行BeforeExec成员函数;同样若struct实现了AfterExecer接口,则在执行action之后会执行AfterExec成员函数。
func main() {
web, err := wego.NewWeb()
if err != nil{
log.Error(err)
return
}
//初始化session
store := cache.NewCookieStore("demo", "1111")
web.Session.Init(store)
//路由注册
web.GET("/login", (*LoginControl).Login)
web.GET("/index", (*IndexControl).Index)
err = web.Run(":8080")
if err != nil {
log.Error(err)
}
}
上面的代码中注册了两个接口型路由器:(*LoginControl).Login,以及(*IndexControl).Index, 首先看看LoginControl的定义:
type LoginControl struct {
}
func (this *LoginControl) Login(c *wego.WebContext) {
c.Session.Set("uid", 1)
c.Session.Save()
c.Redirect(302, "/index")
}
接下来是IndexControl的定义:
type IndexControl struct {
BaseControl
}
func (this *IndexControl) Index(c *wego.WebContext) {
c.WriteTextF(200, "uid=%d", this.GetUID(c))
}
在IndexControl中组合了BaseControl,因此IndexControl也继承了BaseControl的方法,通常将一些公用的功能添加到BaseControl中,例如:登陆验证。BaseControl的实现如下:
type BaseControl struct {
}
func (this *BaseControl) SetUID(c *wego.WebContext, uid int64) {
c.Data.Set("uid", uid)
}
func (this *BaseControl) GetUID(c *wego.WebContext) int64{
uid , _ := c.Data.GetInt64("uid")
return uid
}
func (this *BaseControl) BeforeExec(c *wego.WebContext) {
val, err := c.Session.GetInt("uid")
if err != nil {
c.Redirect(302, "/login")
}
this.SetUID(c, val)
}
在BaseControl的BeforeExec成员函数中读取session数据:uid,若不存在uid则跳转到/login页面。
beforeRoute拦截器只能是全局拦截器,before_exec、after_exec可以是以下类型:
若同时设置了以上拦截器,则优先级为:
函数拦截器->接口拦截器->路由组拦截器->全局拦截器
过滤器是wego框架的中间件,是一种可以拦截http请求-响应生命周期的特殊函数,在请求-响应生命周期中可以注册多个中间件,
每个中间件执行不同的功能,一个中间执行完再执行下一个中间件。
通过AddFilter设置全局过滤器或者是给路由组设置过滤器,下面是一个设置全局过滤器的例子:
func main() {
web, err := wego.InitWeb("./app.conf")
if err != nil{
log.Error(err)
return
}
//设置生成请求ID的过滤器
web.AddFilter("GenRequestID", filters.GenRequestID())
web.GET("/reqid", ShowHtml)
err = web.Run(":8080")
if err != nil {
log.Error(err)
}
}
func ShowHtml(c *wego.WebContext) {
c.UseGzip(true).WriteHTML(200, "./content.html", nil)
}
通过下面一个例子,读者可以了解如何自定义一个过滤器中间件:
func genRequestID() string {
b := make([]byte, 16)
_, err := rand.Read(b)
if err != nil {
return ""
}
sid := hex.EncodeToString(b)
return sid
}
const keyRequestID = "X-Request-ID"
func GenRequestID() wego.HandlerFunc {
return func(c *wego.WebContext) {
rid :=c.Input.GetHeader(keyRequestID)
if rid == "" {
rid = genRequestID()
c.Input.Header.Add(keyRequestID, rid)
}
c.SetHeader(keyRequestID, rid)
c.Next()
}
}
过滤器可以是以下类型:
若同时设置了以上过滤器,则优先级为:
1)首先检查所属路由组存在过滤器, 若存在这使用当前路由组过滤器;
2)若不存在,则检查上级路由组是否有过滤器,若存在则使用上级路由组过滤器;
......
3)最后查看是否存在全局过滤器,若存在则使用全局过滤器