有没有办法列出context.Context中的键?

时间:2019-02-28 13:20:11

标签: go

因此,我有一个context.Context(https://golang.org/pkg/context/)变量,有什么办法可以列出该变量保存的所有键?

2 个答案:

答案 0 :(得分:7)

可以列出上下文的内部。使用不安全的反射并使用该信息找出上下文键和/或查看所需的信息是否在上下文中。

有一些陷阱,例如上下文实现是否为它不会在此处显示的键返回一个硬编码的值,并且对于如何使用键实际访问这些值可能还不清楚。

这不是我在生产中运行的。但就我而言,我需要能够检查上下文。上下文可以更好地理解上下文中包含的信息。

func printContextInternals(ctx interface{}, inner bool) {
    contextValues := reflect.ValueOf(ctx).Elem()
    contextKeys := reflect.TypeOf(ctx).Elem()

    if !inner {
        fmt.Printf("\nFields for %s.%s\n", contextKeys.PkgPath(), contextKeys.Name())
    }

    if contextKeys.Kind() == reflect.Struct {
        for i := 0; i < contextValues.NumField(); i++ {
            reflectValue := contextValues.Field(i)
            reflectValue = reflect.NewAt(reflectValue.Type(), unsafe.Pointer(reflectValue.UnsafeAddr())).Elem()

            reflectField := contextKeys.Field(i)

            if reflectField.Name == "Context" {
                printContextInternals(reflectValue.Interface(), true)
            } else {
                fmt.Printf("field name: %+v\n", reflectField.Name)
                fmt.Printf("value: %+v\n", reflectValue.Interface())
            }
        }
    } else {
        fmt.Printf("context is empty (int)\n")
    }
}

示例:

func Ping(w http.ResponseWriter, r *http.Request) {
    printContextInternals(r.Context(), false)
    /* Prints
        Fields for context.valueCtx
        context is empty (int)
        field name: key
        value: net/http context value http-server
        field name: val
        value: &{Addr::20885 Handler:0xc00001c000 TLSConfig:0xc000001c80 ReadTimeout:0s ReadHeaderTimeout:0s WriteTimeout:0s IdleTimeout:0s MaxHeaderBytes:0 TLSNextProto:map[h2:0x12db010] ConnState:<nil> ErrorLog:<nil> BaseContext:<nil> ConnContext:<nil> disableKeepAlives:0 inShutdown:0 nextProtoOnce:{done:1 m:{state:0 sema:0}} nextProtoErr:<nil> mu:{state:0 sema:0} listeners:map[0xc00015a840:{}] activeConn:map[0xc000556fa0:{}] doneChan:<nil> onShutdown:[0x12e9670]}
        field name: key
        value: net/http context value local-addr
        field name: val
        value: [::1]:20885
        field name: mu
        value: {state:0 sema:0}
        field name: done
        value: 0xc00003c2a0
        field name: children
        value: map[context.Background.WithValue(type *http.contextKey, val <not Stringer>).WithValue(type *http.contextKey, val [::1]:20885).WithCancel.WithCancel:{}]
        field name: err
        value: <nil>
        field name: mu
        value: {state:0 sema:0}
        field name: done
        value: <nil>
        field name: children
        value: map[]
        field name: err
        value: <nil>
        field name: key
        value: 0
        field name: val
        value: map[]
        field name: key
        value: 1
        field name: val
        value: &{handler:0x151cf50 buildOnly:false name: err:<nil> namedRoutes:map[] routeConf:{useEncodedPath:false strictSlash:false skipClean:false regexp:{host:<nil> path:0xc0003d78f0 queries:[]} matchers:[0xc0003d78f0 [GET POST]] buildScheme: buildVarsFunc:<nil>}}

    */


    printContextInternals(context.Background(), false)
    /* Prints
        Fields for context.emptyCtx
        context is empty (int)
    */
}

答案 1 :(得分:2)

没有办法列出context.Context的所有键。因为该类型只是一个接口。那是什么意思?

通常,变量可以包含具体类型或接口。接口类型的变量上没有任何具体的类型信息。因此,如果接口为空(interface{})或context.Context,则没有任何区别。因为它们可能是实现该接口的许多不同类型。该变量没有具体类型。只是抽象而已。

如果使用反射,则可以观察设置为该变量(具有接口类型)的字段和所有类型的方法。但是,如何实现方法Value(key interface{}) interface{}的逻辑是不确定的。它不必是地图。您还可以使用切片,数据库,自己的哈希表类型,...

因此,没有列出所有值的通用方法。