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
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{}}