简体   繁体   English

在 Go HTTP 处理程序中使用未导出的结构键获取上下文值时为零

[英]Context value is nil when getting it with unexported struct key in Go HTTP handlers

Any help here is appreciated.任何帮助在这里表示赞赏。 I'm sure that I'm missing something really basic.我确定我错过了一些非常基本的东西。

The problem I have is I am trying to get a value out of context in a demo web application, and I'm receiving the error:我遇到的问题是我试图在演示 web 应用程序中获取上下文之外的值,并且我收到错误:

2021/04/11 11:35:54 http: panic serving [::1]:60769: interface conversion: interface {} is nil, not []string

In my main function I'm setting the context with the following:在我的主要 function 中,我使用以下内容设置上下文:

package main

type ctxKey struct{}

func someHttpHandleFunc() {
  // .....
  ctx := context.WithValue(r.Context, ctxKey{}, matches[1:])
  route.handle(w, r.WithContext(ctx))
}

Then in my handler, I have the following:然后在我的处理程序中,我有以下内容:

package some_package

type ctxKey struct{}
func getField(r *http.Request, index int) string {
    fields := r.Context().Value(ctxKey{}).([]string)
    return fields[index]
}

I know that I'm missing something simple because if I try the above code and put my getField() function within the package main everything works.我知道我遗漏了一些简单的东西,因为如果我尝试上面的代码并将我的getField() function 放在package main中,一切正常。

For reference, this is a learning exercise, I'm trying to teach myself Go routing.作为参考,这是一个学习练习,我正在尝试自学 Go 路由。 I do know that there are routing packages available - but my goal is to learn.我确实知道有可用的路由包 - 但我的目标是学习。 I'm trying my best to follow along with Different approaches to HTTP routing in Go .我正在尽力遵循Go 中 HTTP 路由的不同方法 I have also read through Pitfalls of context values and how to avoid or mitigate them in Go .我还通读了上下文值的陷阱以及如何在 Go 中避免或减轻它们 The latter seems to directly address the problem I'm having, but I can't seem to figure out how to solve it based on what is there.后者似乎直接解决了我遇到的问题,但我似乎无法根据现有的情况弄清楚如何解决它。

Defined struct types defined in different packages are different .不同包中定义的结构体类型是不同的

package main

type ctxKey struct{}

is not the same type as不是同一类型

package some_package

type ctxKey struct{}

To make it intuitively clearer, think that if you were to reference these types from a third package — let's pretend the types were exported — you would have to import the corresponding package and use the appropriate selector:为了使其直观更清晰,请考虑如果您要从第三个 package 引用这些类型 - 假设这些类型已导出 - 您将必须导入相应的 package 并使用适当的选择器:

package baz

import (
   "myproject/foo"
   "myproject/some_package"
)

func doBaz() {
    foo := foo.CtxKey{}
    bar := some_package.CtxKey{}
}

Now, the implementation of Value(key interface{}) uses the comparison operator == to determine whether the supplied key matches:现在, Value(key interface{})的实现使用比较运算符==来确定提供的键是否匹配:

func (c *valueCtx) Value(key interface{}) interface{} {
    if c.key == key {
        return c.val
    }
    // then it checks if the key exists on the parent
    return c.Context.Value(key)
}

Which implies that the types also must match.这意味着类型也必须匹配。 From the specs, comparison operators从规范,比较运算符

In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.在任何比较中,第一个操作数必须可分配给第二个操作数的类型,反之亦然。

And clearly in your example ctxKey struct{} declared in main is not assignable to ctxKey struct{} declared in some_package , and vice-versa, because their types differ.显然,在您的示例中,在main中声明的ctxKey struct{}不能分配给在 some_package 中声明的some_package ctxKey struct{} ,反之亦然,因为它们的类型不同。

To solve your error, make sure the keys used when setting and getting the context value are the same type.要解决您的错误,请确保设置和获取上下文值时使用的键类型相同。 The best way, also to ensure proper encapsulation, would probably be to set and get the context value from the same package:最好的方法,也是为了确保正确封装,可能是从同一个 package 设置和获取上下文值:

package some_ctx_helper_pkg

// unexported, to ensure encapsulation
type ctxKey struct{}

func Set(ctx context.Context, value interface{}) context.Context {
    return context.WithValue(ctx, ctxKey{}, value)
}

func Get(ctx context.Context) interface{} {
    return ctx.Value(ctxKey{})
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM