我刚刚阅读this blog post关于创建函数类型并在该函数上实现.ServeHTTP()
方法以便能够处理错误。例如:
type appError struct {
Error error
Message string
Code int
}
type appHandler func(http.ResponseWriter, *http.Request) *appError
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
func init() {
http.Handle("/view", appHandler(viewRecord)) //viewRecord is an appHandler function
}
我喜欢这种方法,但我无法在概念上弄清楚如何通过处理程序层包含上下文对象。例如:
func init() {
http.Handle("/view", AuthHandler(appHandler(viewRecord)))
}
AuthHandler
可能会创建一个&SessionToken{User: user}
对象,并在每个请求的context.Context对象中设置该对象。我无法解决如何将其传达给viewRecord
处理程序的问题。想法?
答案 0 :(得分:11)
我可以想到几种方法来做到这一点。
首先,您可以更改签名以接受上下文
type appHandler func(http.ResponseWriter, *http.Request, context.Context) *appError
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r, nil); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
现在我假设AuthHandler与身份验证有关,并在上下文对象中设置用户。
您可以做的是创建另一个设置上下文的类型处理程序。像这样
type authHandler func(http.ResponseWriter, *http.Request, context.Context) *appError
func (fn authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// setup authentication here
uid := 1
// setup the context the way you want
parent := context.TODO()
ctx := context.WithValue(parent, userIdKey, uid)
if e := fn(w, r, ctx); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
这样你可以用以下方式使用它
func init() {
http.Handle("/view", appHandler(viewRecord)) // don't require authentication
http.Handle("/viewAuth", authHandler(viewRecord)) // require authentication
}
这是完整的代码
package main
import (
"fmt"
"net/http"
"code.google.com/p/go.net/context"
)
type appError struct {
Error error
Message string
Code int
}
type key int
const userIdKey key = 0
type appHandler func(http.ResponseWriter, *http.Request, context.Context) *appError
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r, nil); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
type authHandler func(http.ResponseWriter, *http.Request, context.Context) *appError
func (fn authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// setup authentication here
uid := 1
// setup the context the way you want
parent := context.TODO()
ctx := context.WithValue(parent, userIdKey, uid)
if e := fn(w, r, ctx); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
func viewRecord(w http.ResponseWriter, r *http.Request, c context.Context) *appError {
if c == nil {
fmt.Fprintf(w, "User are not logged in")
} else {
uid := c.Value(userIdKey)
fmt.Fprintf(w, "User logged in with uid: %d", uid)
}
return nil
}
func init() {
http.Handle("/view", appHandler(viewRecord)) // viewRecord is an appHandler function
http.Handle("/viewAuth", authHandler(viewRecord)) // viewRecord is an authHandler function
}
func main() {
http.ListenAndServe(":8080", nil)
}
您可以创建
,而不是传递上下文var contexts map[*http.Request]context.Context
并使用view
获取contexts[r]
中的上下文。
但由于map不是线程安全的,因此必须使用互斥锁保护对映射的访问。
猜猜看,这是大猩猩的背景为你做的,我认为这是更好的方法
https://github.com/gorilla/context/blob/master/context.go#l20-28
这是完整的代码
package main
import (
"fmt"
"net/http"
"github.com/gorilla/context"
)
type appError struct {
Error error
Message string
Code int
}
type key int
const userIdKey key = 0
type appHandler func(http.ResponseWriter, *http.Request) *appError
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
type authHandler func(http.ResponseWriter, *http.Request) *appError
func (fn authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// setup authentication here
uid := 1
context.Set(r, userIdKey, uid)
if e := fn(w, r); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
func viewRecord(w http.ResponseWriter, r *http.Request) *appError {
if uid, ok := context.GetOk(r, userIdKey); !ok {
fmt.Fprintf(w, "User are not logged in")
} else {
fmt.Fprintf(w, "User logged in with uid: %d", uid)
}
return nil
}
func init() {
http.Handle("/view", appHandler(viewRecord)) // don't require authentication
http.Handle("/viewAuth", authHandler(viewRecord)) // require authentication
}
func main() {
http.ListenAndServe(":8080", nil)
}
您也可以选择包装函数而不是auth的类型函数
func AuthHandler(h appHandler) appHandler {
return func(w http.ResponseWriter, r *http.Request) *appError {
// setup authentication here
uid := 1
context.Set(r, userIdKey, uid)
return h(w, r)
}
}
func init() {
http.Handle("/view", appHandler(viewRecord)) // don't require authentication
http.Handle("/viewAuth", appHandler(AuthHandler(viewRecord))) // require authentication
}
答案 1 :(得分:2)
使用自Go 1.7以来可用的r.Context()
。