-1

我想WithContext为 struct 编写一个方法,并从net/http's中获取灵感Request.WithContext

Request.WithContext我的问题是:如果上下文为零,为什么会恐慌:

func (r *Request) WithContext(ctx context.Context) *Request {
    if ctx == nil {
        panic("nil context")
    }
    ...
}

我也应该这样吗?

有关我为什么要创建WithContext方法的更多上下文:我正在实现一个接口,该接口在其签名中不提供上下文参数,但相信实现需要它。

更具体地说,我正在编写一个 Redis 后端,用于gorilla/session使用Go 的官方 Redis 客户端,其中GetandSet方法采用context.Context

这个想法是,我的 redis 存储将在需要时使用新的上下文对象进行浅拷贝,然后使用:

type redisStore struct {
    codecs  []securecookie.Codec
    backend Backend // custom interface for Redis client
    options *sessions.Options
    ctx     context.Context
}

func (s *redisStore) WithContext(ctx context.Context) *redisStore {
    if ctx == nil {
        panic("nil context")
    }
    s2 := new(redisStore)
    *s2 = *s
    s2.ctx = ctx
    return s2
}

// Backend

type Backend interface {
    Set(context.Context, string, interface{}) error
    Get(context.Context, string) (string, error)
    Del(context.Context, string) error
}
4

2 回答 2

1

恐慌的目的是“快速失败”并拒绝nil上下文而不更改函数签名。

如果函数没有恐慌,那么它必须返回错误以拒绝错误的输入:

func (r *Request) WithContext(ctx context.Context) (*Request, error) {
    if ctx == nil {
        return nil, errors.New("nil ctx")
    }
    ...
}

然后调用此函数的人必须处理错误以避免使用无效请求:

request, err = request.WithContext(nil)
if err != nil {
   
}

通过处理错误,您将引入控制流分支,并且您会丢失方法链接。您也不能立即WithContext在函数参数中使用返回值:

// cannot do, because WithContext returns an error too
data, err := fetchDataWithContext(request.WithContext(ctx), otherParam)

它还会创建一个错误实例,最终将被垃圾收集。仅仅因为说“不要给我一个零上下文”,这一切都很麻烦,可用性差和不必要的分配。

关于创建带上下文的redis存储,上下文文档很清楚:

包上下文定义了 Context 类型,它携带跨越 API 边界和进程之间的截止日期、取消信号和其他请求范围的值。

重要的细节是request-scoped。所以在redis客户端本身设置上下文是违背这个建议的。您应该在每次 get/set 调用时传递上下文值。

于 2021-11-14T07:52:39.340 回答
0

如果客户端关闭连接,则取消 HTTP 请求的上下文。当上下文被取消时,它的所有子上下文也被取消,所以一个 nil 上下文会恐慌。因此,您不能将 nil 上下文传递给WithContext.

您的 redis 存储是否应该恐慌取决于您将如何使用该上下文。在结构中包含上下文通常不是一个好主意。一种可接受的方法是结构本身是上下文。应该为每个调用创建上下文,应该在该调用期间存在,然后丢弃。

于 2021-11-14T05:25:44.353 回答