代码之家  ›  专栏  ›  技术社区  ›  ceth

将上下文从web请求的控制器传递到数据库层

  •  0
  • ceth  · 技术社区  · 6 年前

    我有休息服务:

    • 每个请求都有一个带有jwt令牌的头
    • 每个控制器从请求中获取参数(变量,body..)并将它们传递给数据层。

    我需要将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 实现我所说的“全局变量”?

    1 回复  |  直到 6 年前
        1
  •  3
  •   tonymke    6 年前

    你在描述上下文。

    原来是 gorilla context package ,它提供一个伪全局上下文对象-本质上是 map[interface{}]interface{} 具有对中间件/控制器/数据层堆栈中的所有参与者都可用的内部引用。

    看这个除了从 an excellent guide to the package (全部归功于作者马特·西尔弗洛克)。

    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.