在这个例子中,我们将克隆HTTP请求上下文及其所有的上下文键/值对。然而,重要的一点是,我们将放弃取消或最后期限的传播。原因是,当响应被返回或上下文因任何原因被取消时,我们不希望我们的上下文被终止并过早地破坏我们的应用程序。
这里有两个选项。第一个是你在一个全新的上下文上手动设置所有的键/值对,并忽略HTTP请求上下文。然而,如果你不知道key/value,那么这就没有用了。第二种选择是我们上面所描述的,所以我们要分离并克隆现有的HTTP请求上下文。
有一点你需要注意的是。如果HTTP请求上下文与你需要上下文的后台任务无关,只需创建一个全新的上下文。不要克隆HTTP请求上下文。
我们的例子很简单。我们将有两个中间件,我们在HTTP请求上下文中设置一些键/值对。我们还将在其上添加超时/截止日期。然后我们将打印出原始和分离/克隆的上下文的样子。
结构
├── context
│ └── detached.go
├── main.go
└── middleware
├── one.go
└── two.go
文件
detached.go
package context
import (
"context"
"time"
)
type detached struct {
ctx context.Context
}
func (detached) Deadline() (time.Time, bool) {
return time.Time{}, false
}
func (detached) Done() <-chan struct{} {
return nil
}
func (detached) Err() error {
return nil
}
func (d detached) Value(key interface{}) interface{} {
return d.ctx.Value(key)
}
func Detach(ctx context.Context) context.Context {
return detached{ctx: ctx}
}
one.go
不要像我下面做的那样设置上下文的键/值,正确使用上下文键:
package middleware
import (
"context"
"net/http"
"time"
)
func One(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "one-key", "one-value")
// This will set a deadline on the context.
ctx, cancel := context.WithTimeout(ctx, time.Millisecond*1)
_ = cancel
h.ServeHTTP(w, r.WithContext(ctx))
})
}
two.go
不要像我在下面做的那样设置上下文键/值,正确使用上下文键:
package middleware
import (
"context"
"net/http"
"time"
)
func Two(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "two-key", "two-value")
// This will set a deadline on the context.
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Millisecond*time.Duration(1)))
_ = cancel
h.ServeHTTP(w, r.WithContext(ctx))
})
}
main.go
package main
import (
"fmt"
"log"
"net/http"
"sport/context"
"sport/middleware"
)
func main() {
log.Println("sport running...")
handler := http.NewServeMux()
handler.HandleFunc("/", home)
log.Fatalln(http.ListenAndServe(":8181", middleware.One(middleware.Two(handler))))
}
func home(w http.ResponseWriter, r *http.Request) {
log.Println("home")
// Detached context.
detachedCtx := context.Detach(r.Context())
detachedDeadline, ok := detachedCtx.Deadline()
fmt.Println("DETACHED ---")
fmt.Println("Deadline():", detachedDeadline, ok)
fmt.Println("Err():", r.Context().Err())
log.Println(detachedCtx.Value("one-key"))
log.Println(detachedCtx.Value("two-key"))
// Original context.
originalCtx := r.Context()
originalDeadline, ok := originalCtx.Deadline()
fmt.Println("ORIGINAL ---")
fmt.Println("Deadline():", originalDeadline, ok)
fmt.Println("Err():", r.Context().Err())
log.Println(originalCtx.Value("one-key"))
log.Println(originalCtx.Value("two-key"))
}
测试
当你调用http://localhost:8181 ,终端的输出应该如下图所示。正如你所看到的,我们分离/克隆的上下文丢弃了超时/截止信息,所以它不会再被取消了:
DETACHED ---
Deadline(): 0001-01-01 00:00:00 +0000 UTC false
Err():
2020/09/15 12:42:14 one-value
2020/09/15 12:42:14 two-value
ORIGINAL ---
Deadline(): 2020-09-15 12:42:14.746067 +0100 BST m=+5.217963805 true
Err():
2020/09/15 12:42:14 one-value
2020/09/15 12:42:14 two-value