3

I cannot find the way to execute this code properly with UNIX:

package main

import (
  "time"
  "github.com/gin-gonic/gin"
  "net/http"
)

type Things struct {
  Name string `json:"name"`
  OneDay time.Time `json:"oneDay"`
}

type Example struct {
  Things []Things `json:"things"`
  Something int `json:"something"`
}

func TestGinGo(c *gin.Context) {
  var example Example
  c.BindJSON(&example)
  c.JSON(http.StatusOK, gin.H{"data": example})
}

func main() {
  r := gin.Default()

  r.POST("/", TestGinGo)

  r.Run("0.0.0.0:8080")
}

I call this endpoint like this:

curl --location --request POST 'localhost:8080' \
--header 'Content-Type: application/json' \
--data-raw '{
    "things": [{
        "name": "bling",
        "oneDay": "2020-01-01T00:00:00Z"
    }],
    "something": 2
}'

The response is correct:

{
    "data": {
        "things": [
            {
                "name": "bling",
                "oneDay": "2020-01-01T00:00:00Z"
            }
        ],
        "something": 2
    }
}

Now I change slightly the code to work with UNIX like this:

package main

import (
  "time"
  "github.com/gin-gonic/gin"
  "net/http"
)

type Things struct {
  Name string `json:"name"`
  OneDay time.Time `json:"oneDay" time_format:"unix"`
}

type Example struct {
  Things []Things `json:"things"`
  Something int `json:"something"`
}

func TestGinGo(c *gin.Context) {
  var example Example
  c.BindJSON(&example)
  c.JSON(http.StatusOK, gin.H{"data": example})
}

func main() {
  r := gin.Default()

  r.POST("/", TestGinGo)

  r.Run("0.0.0.0:8080")
}

And I call it like this:

curl --location --request POST 'localhost:8080' \
--header 'Content-Type: application/json' \
--data-raw '{
    "things": [{
        "name": "bling",
        "oneDay": 1589898758007
    }],
    "something": 2
}'

And I get this error now (400 bad format):

{"data":{"things":[{"name":"bling","oneDay":"0001-01-01T00:00:00Z"}],"something":0}}

I get into the library... and I see that the code is there to use "unix":

https://github.com/gin-gonic/gin/blob/master/binding/form_mapping.go#L272

I would really like to use unix, because many languages don't need a library for using unix, and I don't want to force an specific format to be used by consumer... And I don't see where I am missing the ball here...

juan garcia
  • 1,326
  • 2
  • 23
  • 56
  • @mkopriva As you see the input is working with a default format RFC3339... I am just making an eco with those values as an output... but the input is not being properly decoded when I say format unix and I provide a number... but this guy did it work properly: https://github.com/gin-gonic/gin/issues/2009 and I don't see how or why – juan garcia May 19 '20 at 15:43
  • https://github.com/gin-gonic/gin/issues/2009#issuecomment-519159298 says they fixed it by using that specific version, what version of gin are you on? – mkopriva May 19 '20 at 15:51
  • go version go1.14.2 darwin/amd64 – juan garcia May 19 '20 at 15:52
  • not go version, the gin version, what is your gin version – mkopriva May 19 '20 at 15:53
  • yeahp "v1.6.3" I am sorry for that one, just to be sure of the method I used to know the version, I used godoc and verified the version from there... and also went to the repo and verified that I got the latest version just a few days ago... yes this is the version – juan garcia May 19 '20 at 16:07
  • will end up adding a PR to the library if it is necessary... but I just want to be sure I am not doing something wrong... or miss using this package... – juan garcia May 19 '20 at 16:10
  • 2
    Looking at the source this seems to be a `form` input only feature, json is not supported. You'll need to use a custom time type instead. – mkopriva May 19 '20 at 16:14
  • thanks i will see how to make that happen, or help a bit with a PR... is a nice lib – juan garcia May 19 '20 at 16:17

1 Answers1

4
  1. time_format tag is only used in form binding, not json;
  2. You can use custom Marshal and Unmarshal functions (see https://pkg.go.dev/encoding/json#example-package-CustomMarshalJSON)
package main

import (
    "encoding/json"
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
    "time"
)

type myTime time.Time

func (mt *myTime) UnmarshalJSON(bs []byte) error {
    var timestamp int64
    err := json.Unmarshal(bs, &timestamp)
    if err != nil {
        return err
    }

    *mt = myTime(time.Unix(timestamp/1000, timestamp%1000*1e6))
    return nil
}

func (mt myTime) MarshalJSON() ([]byte, error) {
    timestamp := time.Time(mt).UnixNano() / 1e6
    log.Println(time.Time(mt).UnixNano())
    return json.Marshal(timestamp)
}

type Timestamp struct {
    OneDay     myTime    `json:"oneDay" form:"oneDay"`
    AnotherDay time.Time `json:"anotherDay" form:"anotherDay" time_format:"unix"`
}

func parseTime(c *gin.Context) {
    var example Timestamp
    if err := c.Bind(&example); err != nil {
        log.Printf("bind timestamp error: %s", err)
    }
    c.JSON(http.StatusOK, gin.H{"data": example})
}

func main() {
    r := gin.Default()

    r.POST("/time", parseTime)

    r.Run("0.0.0.0:8080")
}
  1. send as json
curl --location --request POST 'localhost:8080/time' \
--header 'Content-Type: application/json' \
--data '{
  "oneDay": 1589898758007,
  "anotherDay": "1589898758"
}'

oneDay is right, anotherDay doesn't work

{"data":{"oneDay":1589898758007,"anotherDay":"0001-01-01T00:00:00Z"}}
  1. send as form
curl --location --request POST 'localhost:8080/time' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data 'oneDay=1589898758007&anotherDay=1589898758'

both are right

{"data":{"oneDay":1589898758007,"anotherDay":"2020-05-19T22:32:38+08:00"}}
  • why does the request format for json or form affects the result of the response? any ideas? – Kelwin Tantono Oct 13 '21 at 14:25
  • because `Bind` check the `Content-Type` of the request. see here https://github.com/gin-gonic/gin/blob/92eeaa4ebbadec2376e2ca5f5749888da1a42e24/context.go#L603 – AlexanderXing Oct 14 '21 at 03:02