我有REST服务:
我需要将JWT令牌从每个请求的标头传递到相应的数据层方法中,如下所示:
func (a *App) UpdateOrder(_ http.ResponseWriter, r *http.Request) (interface{}, error) {
bodyData := new(models.Order)
err = json.NewDecoder(r.Body).Decode(&bodyData)
if err != nil {
return nil, err
}
user, err := a.Saga.GetUserByToken(r.Header.Get("Authorization")) // here
// error handling ...
a.DbLayer.UpdateOrder(id, bodyData, user) // and there
}
在这种情况下,我必须为每个控制器编写相同的代码以按令牌获取用户,并将该用户显式传递到数据库层。
是否有一种方法可以针对每个请求传递此用户,而无需在每个控制器中编写此代码?
我了解中间件,并且可以在中间件中通过令牌吸引用户。但是如何将该用户从中间件传递到相应的数据库级方法?
我可能正在寻找goroutine之类的“全局变量”之类的东西吗?我可以在中间件中获取用户并将其设置为“全局变量”之类的东西。我可以在数据库层中获取此“全局变量”的值。但是对于当前的Web请求,它必须是“全局变量”,并且并发的Web请求不能相互影响。
Go,http模块或gorilla\mux
中是否存在某种机制来实现我所谓的“全局变量”?
答案 0 :(得分:3)
您正在描述上下文。
最初有gorilla context package,它提供了一个伪全局上下文对象-本质上是map[interface{}]interface{}
,具有中间件/控制器/数据层堆栈中的所有播放器固有的引用。
除了an excellent guide to the package以外,请参见其他内容(作者全部致谢,Matt Silverlock)。
type contextKey int
// Define keys that support equality.
const csrfKey contextKey = 0
const userKey contextKey = 1
var ErrCSRFTokenNotPresent = errors.New("CSRF token not present in the request context.")
// We'll need a helper function like this for every key:type
// combination we store in our context map else we repeat this
// in every middleware/handler that needs to access the value.
func GetCSRFToken(r *http.Request) (string, error) {
val, ok := context.GetOk(r, csrfKey)
if !ok {
return "", ErrCSRFTokenNotPresent
}
token, ok := val.(string)
if !ok {
return "", ErrCSRFTokenNotPresent
}
return token, nil
}
// A bare-bones example
func CSRFMiddleware(h http.Handler) http.Handler {
return func(w http.ResponseWriter, r *http.Request) {
token, err := GetCSRFToken(r)
if err != nil {
http.Error(w, "No good!", http.StatusInternalServerError)
return
}
// The map is global, so we just call the Set function
context.Set(r, csrfKey, token)
h.ServeHTTP(w, r)
}
}
启动大猩猩软件包后,已将context package添加到标准库中。稍有不同,因为上下文不再是伪全局的,而是在方法之间传递。在此情况下,上下文将附加到初始请求-可通过request.Context
获得。处理程序下面的层可以接受上下文值作为其签名的一部分,并从中读取值。
这是一个简化的示例:
type contextKey string
var (
aPreSharedKey = contextKey("a-preshared-key")
)
func someHandler(w http.ResponseWriter, req *http.Request) {
ctx := context.WithValue(req.Context, aPreSharedKey, req.Header.Get("required-header"))
data, err := someDataLayerFunction(ctx)
if err != nil {
fmt.Fprintf(w, "uhoh", http.StatusBadRequest)
return
}
fmt.Fprintf(w, data, http.StatusOK)
}
func someDataLayerFunction(ctx context.Context) (string, error) {
val, ok := ctx.Value(aPreSharedKey).(string)
if !ok {
return nil, errors.New("required context value missing")
}
return val
}
有关更多详细信息和较少人为的示例,请查看Google的excellent blog on the context package's use.