上下文
Context - 上下文
原文:https://echo.labstack.com/docs/context
echo.Context
表示当前 HTTP 请求的上下文。它保存了请求和响应的引用、路径、路径参数、数据、已注册的处理程序以及读取请求和写入响应的 API。由于 Context 是一个接口,可以轻松地通过自定义 API 扩展它。
扩展
定义一个自定义context
1
2
3
4
5
6
7
8
9
10
11
| type CustomContext struct {
echo.Context
}
func (c *CustomContext) Foo() {
println("foo")
}
func (c *CustomContext) Bar() {
println("bar")
}
|
创建一个中间件来扩展默认context
1
2
3
4
5
6
| e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
cc := &CustomContext{c}
return next(cc)
}
})
|
注意事项
这个中间件应该在其他中间件之前注册。
注意事项
在路由运行之前(Pre)不能在中间件中定义自定义上下文。
在处理程序中使用
1
2
3
4
5
6
| e.GET("/", func(c echo.Context) error {
cc := c.(*CustomContext)
cc.Foo()
cc.Bar()
return cc.String(200, "OK")
})
|
并发
注意事项
Context
不能在处理请求的 goroutine 之外被访问。原因有两个:
Context
具有从多个 goroutine 执行时容易出现问题的函数。因此,只能有一个 goroutine 访问它。- Echo 使用池来创建
Context
。当请求处理完成后,Echo 将 Context
返回到池中。
关于这个原因,参考 issue 1908 中发生的“警示故事(cautionary tale)”。并发是复杂的。在使用 goroutine 时要注意这个陷阱(pitfall)。
解决方案
使用通道
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| func(c echo.Context) error {
ca := make(chan string, 1) // 为了防止该通道阻塞,大小设置为1。
r := c.Request()
method := r.Method
go func() {
// 此函数不能操作 Context。
fmt.Printf("Method: %s\n", method)
// 执行一些耗时操作...
ca <- "Hey!"
}()
select {
case result := <-ca:
return c.String(http.StatusOK, "Result: "+result)
case <-c.Request().Context().Done(): // 检查上下文。
// 如果到达这里,说明上下文已取消(达到了超时等)。
return nil
}
}
|