gin-gonic / gin

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.

Home Page:https://gin-gonic.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Custom time.Time type can not use bindQuery get a value 2

feiyangbeyond opened this issue · comments

commented

I have a new type MyTime, and it has implements encoding/json.Unmarshaler.

const defaultTimeLayout = "2006-01-02 15:04:05"

type MyTime time.Time

func (t *MyTime) UnmarshalJSON(data []byte) error {
	var v interface{}
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}
	switch value := v.(type) {
	case string:
		var err error
		tt, err := time.ParseInLocation(defaultTimeLayout, value, time.Local)
		if err != nil {
			return err
		}
		*t = MyTime(tt)
		return nil
	default:
		return errors.New("invalid time format")
	}
}

func (t MyTime) MarshalJSON() ([]byte, error) {
	return json.Marshal(time.Time(t).Format(defaultTimeLayout))
}

And my struct define:

type MyReq struct {
	Time MyTime `json:"time" form:"time" binding:"required" time_format:"2006-01-02 15:04:05"`
}

When I use http get query , the same error occurred. And after changing MyReq.Time type to time.Time, it works.

I hope it works with MyTime.

Did I use it improperly?

Originally posted by @feiyangbeyond in #3895 (comment)

--

Why I use MyTime?

I wish to control the type of time in json serialization/formatting, I use MyTime in get/post request to keep it consistent

I don't think this issue is about gin, but you can refer to the source code of Time.time and write a custom type of JSON serialization and deserialization method.

// MarshalJSON implements the json.Marshaler interface.
// The time is a quoted string in the RFC 3339 format with sub-second precision.
// If the timestamp cannot be represented as valid RFC 3339
// (e.g., the year is out of range), then an error is reported.
func (t Time) MarshalJSON() ([]byte, error) {
	b := make([]byte, 0, len(RFC3339Nano)+len(`""`))
	b = append(b, '"')
	b, err := t.appendStrictRFC3339(b)
	b = append(b, '"')
	if err != nil {
		return nil, errors.New("Time.MarshalJSON: " + err.Error())
	}
	return b, nil
}

// UnmarshalJSON implements the json.Unmarshaler interface.
// The time must be a quoted string in the RFC 3339 format.
func (t *Time) UnmarshalJSON(data []byte) error {
	if string(data) == "null" {
		return nil
	}
	// TODO(https://go.dev/issue/47353): Properly unescape a JSON string.
	if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
		return errors.New("Time.UnmarshalJSON: input is not a JSON string")
	}
	data = data[len(`"`) : len(data)-len(`"`)]
	var err error
	*t, err = parseStrictRFC3339(data)
	return err
}
commented

@RedCrazyGhost

I'm not sure if it's a gin error. Let me tell you the results of the investigation.

The program has thrown an error before it reaches the custom time type I wrote.

This error only occurs when making a get request and using gin to bind query. The error is invalid character '-' after top-level value

My struct:

type Foo struct {
    Bar string `form:"bar"`
    Time MyTime `form:"time"` // use MyTime 
}

When I send get request like /foo?bar=some&time=2024-04-17 13:21:45 , gin uses json.Unmarshal to convert 2024-04-17 13:21:45 to MyTime,

Since 2024-04-17 13:21:45 is not a normal json string, using json.Unmarshal will definitely report an error.

I think the error root cause is binding single parameter value as json when binding parameters on get request.

I can add "" before and after the time field when sending a get request, this way you can avoid this error.