zeromicro / go-zero

A cloud-native Go microservices framework with cli tool for productivity.
https://go-zero.dev
MIT License
29.17k stars 3.95k forks source link

A bug of function httpx.ParseJsonBody while parse []interface{} #4259

Open iporer opened 3 months ago

iporer commented 3 months ago

Describe the bug A bug of function httpx.ParseJsonBody. If I use A []interface{}json:"a"` to parse, httpx.ParseJsonBody will panic with errorreflect: Key of non-map type interface {}`, but json.Unmarshal could parse successfully.

Otherwise, if I use A []Bjson:"a" to parse, httpx.ParseJsonBody could parse successfully.

To Reproduce Steps to reproduce the behavior, if applicable:

  1. The code is

        var req types.Req
        if err := httpx.ParseJsonBody(r, &req); err != nil {
            httpx.ErrorCtx(r.Context(), w, err)
            return
        }

body of requese:

    {"a":[{"b":"1"}]}
  1. The error is

    reflect: Key of non-map type interface {}

Expected behavior Could parse json successfully while using interface{} array.

Screenshots use json.Unmarshal could parse successfully. image

Environments (please complete the following information):

More description Add any other context about the problem here.

iporer commented 3 months ago

More code to test json.Unmarshal:

        var requestBody []byte
        requestBody, _ = io.ReadAll(r.Body)

        r.Body.Close()
        r.Body = io.NopCloser(bytes.NewBuffer(requestBody))

        logx.Infof("request body: %s", string(requestBody))

        var req types.Req
        json.Unmarshal(requestBody, &req)
        if err := httpx.ParseJsonBody(r, &req); err != nil {
            httpx.ErrorCtx(r.Context(), w, err)
            return
        }
streamsunshine commented 3 months ago

I had the same problem

type Info struct {
    Name string `json:name`
}
type Req struct {
     Infos          []Info  `json:"infos"`
}
http body is  
{
  "infos": "[{}]"
}

then, httpx.PaseJsonBody will report reflect: Key of non-map Info i find something wrong at https://github.com/zeromicro/go-zero/blob/master/core/mapping/unmarshaler.go#L614

case valueKind == reflect.String && typeKind == reflect.Slice:
        return u.fillSliceFromString(fieldType, value, mapValue, fullName)

i think it should return type mismatch error rather than continue parsing

streamsunshine commented 3 months ago

func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect.Value, mapValue any, fullName string) error {

switch v := mapValue.(type) {
case fmt.Stringer:
    if err := jsonx.UnmarshalFromString(v.String(), value.Addr().Interface()); err != nil {
        return err
    }
case string:
    if err := jsonx.UnmarshalFromString(v, value.Addr().Interface()); err != nil {
        return err
    }
default:
    return errUnsupportedType
}

return nil

} https://github.com/zeromicro/go-zero/blob/master/core/mapping/unmarshaler.go#L222 seems a little complicated. The above code seems to work