Description
While the implement of binding.FormPost
will trying to usejson.Unmarshal
to deal with custom or unknown type.
https://github.com/gin-gonic/gin/blob/3100b7cb05a8072b76d31686d8a7b4f9b12df4be/binding/form_mapping.go#L212
But it will failed if the data is represent as string in json, since application/x-www-form-urlencoded
won't quote string data. These data will not pass the json's validation.
And the json.Unmarshal
for custom data feature is totally broken here. Since we have no way to known if it a string type or other type in application/x-www-form-urlencoded
format.
I suggest we can add a logic here if it parse custom type failed with json,then add a quote then try again.
How to reproduce
package main
import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"net/http"
"time"
)
func main() {
g := gin.Default()
g.POST("/hello", func(c *gin.Context) {
data := TestForm{}
err := c.MustBindWith(&data,binding.FormPost)
fmt.Printf("%+v\n",c.Request.PostForm)
if err != nil {
fmt.Printf("%+v\n",err)
return
}
c.String(http.StatusOK,data.Time.String())
})
g.Run(":9000")
}
type TestForm struct{
Time CustomDateTime
}
type CustomDateTime time.Time
const CustomDateTimeFormat = "2006/01/02 15:04:05"
func (t CustomDateTime) String() string {
return time.Time(t).Format(CustomDateTimeFormat)
}
func (t *CustomDateTime) UnmarshalJSON(data []byte) error {
var value string
err := json.Unmarshal(data, &value)
if err != nil {
return err
}
parseTime, err := time.Parse(CustomDateTimeFormat, value)
if err != nil {
return err
}
*t = CustomDateTime(parseTime)
return nil
}
Expectations
$ curl -H "Content-Type: application/x-www-form-urlencoded" -X POST -d "Time=2020%2F09%2F23+13%3A20%3A49" http://localhost:9000/hello
2020/09/23 13:20:49
Actual result
$ curl -H "Content-Type: application/x-www-form-urlencoded" -X POST -d "Time=2020%2F09%2F23+13%3A20%3A49" http://localhost:9000/hello
<400 Bad Request>
console output
GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] POST /hello --> main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on :9000
map[Time:[2020/09/23 13:20:49]]
invalid character '/' after top-level value
[GIN] 2020/09/23 - 14:59:47 | 400 | 0s | ::1 | POST "/hello"
Environment
- go version: 1.14
- gin version (or commit ref): v1.6.3
- operating system: windows 10
Comment From: kuoruan
If you only want to custom the datetime format, here is a tag already exist in Gin: time_format
https://github.com/gin-gonic/gin/blob/65ed60ed1334140d8583a3d0533cea76c66b69fe/binding/form_mapping.go#L280-L329
Comment From: eloyekunle
This issue is fixed by either https://github.com/gin-gonic/gin/pull/3045 or https://github.com/gin-gonic/gin/pull/2511, but they're still not merged yet.
Related issues: https://github.com/gin-gonic/gin/issues/3060, https://github.com/gin-gonic/gin/issues/2673 and https://github.com/gin-gonic/gin/issues/2631.
Comment From: appleboy
I will take it.
Comment From: doriotp
Hi @LaysDragon is this issue still persist?