Open kartavya-ramnani opened 4 years ago
type Request struct {
AppCode string `header:"appCode" json:"-" `
SomeID string `header:"-" json:"someId"`
UserID string `header:"userId" json:"-"`
EmailID string `header:"-" json:"emailId"`
}
just use "-" to remove tag that you don't want.
and binding:"required
should not be used if you want to ignore one tag
and
binding:"required
should not be used if you want to ignore one tag
what if I would like to use validation? OP asks how to combine headers and json/form values in one struct. As far as I see it is not possible right now.
I guess you could create an extra function like:
type Request struct {
AppCode string `header:"appCode" json:"-" `
SomeID *string `header:"-" json:"someId"`
UserID string `header:"userId" json:"-"`
EmailID string `header:"-" json:"emailId"`
}
func (r *Request) Validate(context *gin.Context) bool {
if r.SomeID == nil {
// do something with context
context.abort()
return false
}
return true
}
maibe it's not ideal, but i've been working this way for some specific stuff and all went good so far
Hey! I've got the same issue..
func (api *api) UpdateStuff(c *gin.Context) {
type updateRequest struct {
User string `form:"user" binding:"required,oneof=foo bar doe"`
UserAgent string `header:"User-Agent" binding:"required"`
}
var update updateRequest
if err := c.BindQuery(&update); err != nil {
_ = c.Error(err)
c.JSON(http.StatusBadRequest, errorContainer{Error: err.Error()})
return
} else if err := c.BindHeader(&update); err != nil {
_ = c.Error(err)
c.JSON(http.StatusBadRequest, errorContainer{Error: err.Error()})
return
}
// ...
c.JSON(http.StatusOK, updateResponse{
Count: count,
})
}
and I always get
{"error":"Key: 'updateRequest.UserAgent' Error:Field validation for 'UserAgent' failed on the 'required' tag"}
I tried without the required field but.. Doesn't help as I wished
Thanks
I think it's not possible to bind a struct which has both json
and header
(or other tags like uri
). Because gin can bind one of them at a time. You can bind header and json fields in two steps like:
type RequestHeaders struct {
AppCode string `header:"appCode" binding:"required"`
UserId string `header:"userId"`
}
type RequestBody struct {
SomeId string `json:"someId" binding:"required"`
EmailId string `json:"emailId"`
}
func bindTest(ctx *gin.Context) {
var body RequestBody
var headers RequestHeaders
err = ctx.ShouldBindJSON(&body)
if err != nil {
err = errors.REQUEST_BIND_FAIL(err, request)
return
}
err = ctx.ShouldBindHeader(&headers)
if err != nil {
err = errors.REQUEST_BIND_FAIL(err, request)
return
}
}
I got curious about this and I like this solution because it keeps everything together but it respects that headers and body needs to be bound separately.
package main
import (
"bytes"
"net/http"
"github.com/gin-gonic/gin"
"github.com/k0kubun/pp"
)
type Request struct {
Headers struct {
UserID int `header:"user-id" json:"-" binding:"required"`
Authorization string `header:"Authorization" json:"-" binding:"required"`
}
Body struct {
Body string `json:"body" binding:"required"`
}
}
func main() {
router := gin.Default()
router.POST("/", func(c *gin.Context) {
// Bind the request
var req Request
if err := c.ShouldBindHeader(&req.Headers); err != nil {
panic(err)
}
if err := c.ShouldBindJSON(&req.Body); err != nil {
panic(err)
}
// Debug print the request
pp.Println(req)
})
go func() {
body := []byte(`{"body":"foo"}`)
req, err := http.NewRequest("POST", "http://localhost:8080/", bytes.NewBuffer(body))
if err != nil {
panic(err)
}
req.Header.Set("user-id", "123")
req.Header.Set("Authorization", "Bearer AccessToken")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}()
router.Run(":8080")
}
Here's what the output looks like.
Nevermind that's kind of dumb just do this lol
func (r *Router) AuthLogoutDevice(c *gin.Context) {
type Request struct {
UserID string `json:"-"`
AuthorizationToken string `json:"-"`
DeviceID string `json:"deviceID"`
}
// Bind the request
var request Request
var err error
request.UserID = c.GetHeader("user-id")
request.AuthorizationToken, err = getAuthorizationTokenFromHeaders(c)
if err != nil {
r.debugError(c, http.StatusInternalServerError, fmt.Errorf("failed to get authorization token from headers: %w", err))
return
}
if err := c.ShouldBindJSON(&request); err != nil {
r.debugError(c, http.StatusInternalServerError, fmt.Errorf("failed to bind JSON: %w", err))
return
}
// TODO
}
Description
I have to place a request in a struct which uses fields from both headers and json body. Is there a way to bind to a struct with json and header struct tags both ? Like a ShouldBindWith which takes Json and header binding both ?
How to reproduce
Code :
Expectations
Actual result
Currently, Im getting a binding:required error from a field which only has a header tag and not json tag when I do ShouldBindJSON.