英文:
Golang json decoding fails to decode interface{}
问题
我正在使用一个库(go-kit),它要求我指定函数来将请求和响应类型编码/解码为JSON。对于编码,很简单:
func EncodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
return json.NewEncoder(w).Encode(response)
}
我将这个函数传递给创建HTTP服务器的函数,它可以正常工作。然而,他们提出的请求处理方法是创建一个单独的函数,形式如下:
func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req UppercaseRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
对于我的应用程序中的每个RPC都需要创建这样一个函数。我真的很希望保持我的代码DRY,避免编写成百上千个几乎相同的方法。因此,我尝试编写一个函数来生成解码给定请求类型的闭包:
func DecodeRequest(req interface{}) httptransport.DecodeRequestFunc {
return func(_ context.Context, r *http.Request) (interface{}, error) {
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
}
可以这样调用这个函数:
DecodeRequest(UppercaseRequest{})
不幸的是,当我这样做时,JSON解码失败,尽管req的类型实际上是mypackage.UppercaseRequest。我不确定接下来该怎么做。有没有办法避免为每个请求类型编写一个方法?有没有办法在运行时帮助Decode函数理解这个类型是什么?提前谢谢!
这里是一个演示问题的Go Playground链接:
https://play.golang.org/p/GgHsLffp1G
英文:
I'm using a library (go-kit) which requires I specify functions to encode / decode my request and response types to/from JSON. For encoding, it is simple:
func EncodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
return json.NewEncoder(w).Encode(response)
}
I pass this function to create the HTTP server and it works fine. However, their proposed approach for requests is to make a separate function of the form:
func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req UppercaseRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
For every single RPC in my application. I would really like to keep my code DRY and avoid having hundreds of almost identical methods. As such, I attempted to write a function to generate closures that decode the given request type:
func DecodeRequest(req interface{}) httptransport.DecodeRequestFunc {
return func(_ context.Context, r *http.Request) (interface{}, error) {
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
}
This function can be called like so:
DecodeRequest(UppercaseRequest{}}
Unfortunately, when I do so, the JSON decoding fails, even though the type of req is in fact mypackage.UppercaseRequest. I'm not sure where to go from here. Is there a way I can avoid having to write a method per request type? Is there some way I can help the Decode function understand what this type is at runtime? Thanks in advance!
Here is a go playground demonstrating the issue:
https://play.golang.org/p/GgHsLffp1G
答案1
得分: 3
根据你展示的代码片段,我认为你遇到了类型断言的问题。我创建了一个示例代码来展示我下面解释的内容。
你将一个 UpperCaseRequest 传递给 DecodeRequest 函数。在这个函数中,参数的类型是 interface{},并且它将该参数的指针传递给了 json 解码器。因此,解码器看到的是一个指向 interface 的指针,而不是指向 UpperCaseRequest 的指针。
这就是为什么解码不正确的原因。然后,尝试对其进行类型断言会失败,因为无法断言两个不同的类型。
因此,在你的代码中,我建议做以下修改:
func DecodeRequest(req interface{}) httptransport.DecodeRequestFunc {
return func(_ context.Context, r *http.Request) (interface{}, error) {
// 注意这里移除了 '&'
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
return nil, err
}
return req, nil
}
}
并像这样调用该函数:
// 注意这里加上了 '&'
DecodeRequest(&UppercaseRequest{})
希望对你有帮助!
英文:
According to the piece of code you are showing us, I think you are facing a type assertion issue. I created a playground to show you what I explain below.
You're passing a UpperCaseRequest to DecodeRequest func. In this func, the argument is of type interface{}, and it passes a pointer of this argument to the json Decoder. So, The decoder sees a pointer to interface, and not a pointer to UpperCaseRequest.
This is why it's not correctly decoded. And then, trying a type assertion on it fails because asserting two different type is not possible.
So, in your code, I'd suggest:
func DecodeRequest(req interface{}) httptransport.DecodeRequestFunc {
return func(_ context.Context, r *http.Request) (interface{}, error) {
// Note the '&' is removed here
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
return nil, err
}
return req, nil
}
}
And call this function like this:
// Note the & is placed here.
DecodeRequest(&UppercaseRequest{}}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论