Hello,
First of all, thanks for your great project, I'm using it a on daily basis and it's just awesome.
I have no clue on how to properly handle response format when validation fails on BindJSON and I want to send back the errors as JSON.
I'm currently having trouble working with context.BindJSON. I have two fields that are required when receiving JSON, let's call them first and second
type params struct {
First string `json:"first" binding:"required"`
Second string `json:"second" binding:"required"`
}
Now when I receive the request, I handle it like this :
func postFirstSecond(c *gin.Context) {
var err error
c.Header("Content-Type", "application/json; charset=utf-8")
p := params{}
if err = c.BindJSON(&p); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "json decoding : " + err.Error(),
"status": http.StatusBadRequest,
})
return
}
}
So first of all, I don't understand why c.JSON() doesn't return the appropriate content-type, I found that when BindJSON fails, it writes the header to be plain text. Anyway my endpoint always returns JSON so I just set the content-type on top of the handler, problem solved (I guess).
When one field is missing, this endpoint returns something like that :
{
"error": "json decoding : Key: 'params.First' Error:Field validation for 'First' failed on the 'required' tag",
"status": 400
}
That's fine. I mean it's not really what I expected but it works.
When both fields are missing, this is what I get back :
{
"error": "json decoding : Key: 'params.First' Error:Field validation for 'First' failed on the 'required' tag\nKey: 'params.Second' Error:Field validation for 'Second' failed on the 'required' tag",
"status": 400
}
Is there a simple way to handle those errors ? I'd like to get something more user-friendly, or at least a separate field for my errors. Something like :
{
"errors": [
"Key: 'params.First' Error:Field validation for 'First' failed on the 'required' tag",
"Key: 'params.Second' Error:Field validation for 'Second' failed on the 'required' tag"
],
"status": 400
}
I'm aware that checking manually the fields should allow me to do that, but the fact is : I feel like the documentation and examples are pretty vague about error handling.
Could someone help me with that ? How do you handle errors generated by Bind or BindJSON ?
@Depado the error returned is actually of type ValidationErrors and you can cast it and then for or range over each FieldError in the map to get separate fields.
Also for those that may be using v9 of validator the definition is no longer a map https://github.com/go-playground/validator/blob/v9/errors.go#L37
Thanks @joeybloggs I'll give that a try 馃憤
I also experience the error that the 'Content-Type' is text/plain instead of 'application/json' when I have a binding error and return a error struct using c.JSON()
Yes you are correct @toefel18 not all errors that can be returned are ValidationErrors some are related to, as you pointed out, Content-Type and also JSON decoding errors.
so one would have to ensure the cast succeeds:
err:= Bind...
ve, ok := err.(validator.ValidationErrors)
if !ok {
// non validation error... handle appropriately.
}
@joeybloggs that is indeed true, but the problem I am referring to is different
if err := c.Bind(&login); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
Output is JSON, response Content-Type is "text/plain". I thought that is what @Depado meant. Should I file another bug request?
I see @toefel18 although I am not a part of the gin team, I'd say yes it does seem like a separate issue.
P.S. may want to check there isn't one open already, for some reason it sounds familiar :)
@joeybloggs, okay I will look into it later today :+1:
The Content-Type thing is a known issue.
@toefel18 @Depado Try c.AbortWithStatusJSON function in gin develop branch. Please ref: #800
Ah great, thanks @Depado @appleboy!
@Depado Does c.AbortWithStatusJSON function resolve your problem abount Content-Type?
Looks like it does but the main point of this issue was about the parsing of the returned error of the c.Bind method.
@depado you can use a middleware to check for any binding errors. There was a long thread about it somewhere here a while ago.
I've encountered similar issue, I have global after middleware that creates json response with errors when errors are present on the context and I also set header to json in that middleware, but because framework writes headers when handling bind errors, its impossible to overwrite them later... My issue #840, I also have a workaround bind function i'm using for now, see my issue for it.
I have the same issue
var ucr messages.UsersCreateRequest
if e := c.BindJSON(&ucr); e != nil {
c.JSON(http.StatusBadRequest, gin.H{
"errors": "someError/nHere/nAndYeatOneError",
})
return
}
then sometimes I'm getting text/plain, but should text/json, sometimes I'm getting text/json
in response.headers.get('content-type')
@mymtw if you want to get Content-Type application/json header when validating failed , you can use ShouldBindWith instead of Bind function that will not call AbortWithError.
ref: https://github.com/gin-gonic/gin/issues/1041#issuecomment-319125864
@easonlin404 thx, works
Everybody can see the PR. https://github.com/gin-gonic/gin/pull/1047
Most helpful comment
@mymtw if you want to get Content-Type
application/jsonheader when validating failed , you can useShouldBindWithinstead ofBindfunction that will not call AbortWithError.ref: https://github.com/gin-gonic/gin/issues/1041#issuecomment-319125864