simplebank icon indicating copy to clipboard operation
simplebank copied to clipboard

Is it possible to bind `uri` and `json` for the same struct?

Open chengr4 opened this issue 2 years ago • 7 comments

Eg. for updateAccount API, How to bind uri and json for the same struct?

type updateAccountRequest struct {
    ID int64 `uri:"id" binding:"required,min=1"`
    Currency string `json:"currency" binding:"required,currency"`
}

func (server *Server) updateArticle(ctx *gin.Context) {
    var req updateAccountRequest
    if err := ctx.ShouldBindUri(&req); err != nil {
        // send 400 Bad Request to the client
        ctx.JSON(http.StatusBadRequest, errorResponse(err))
        return
    }

    if err := ctx.ShouldBindJSON(&req); err != nil {
        // send 400 Bad Request to the client
        ctx.JSON(http.StatusBadRequest, errorResponse(err))
        return
    }
}

the code above does not work.

Does anyone know? Thanks in advance

chengr4 avatar Jun 13 '23 09:06 chengr4

I use result here: https://github.com/gin-gonic/gin/issues/1824#issuecomment-475968345 but I wonder whether there is a better solution

chengr4 avatar Jun 20 '23 07:06 chengr4

I use result here: gin-gonic/gin#1824 (comment) but I wonder whether there is a better solution

type updateAccountRequest struct {
	ID      int64 `uri:"id" binding:"required,min=1"`
	Balance int64 `json:"balance"`
}

func (server *Server) updateAccountHandler(ctx *gin.Context) {
	var req updateAccountRequest
	if err := ctx.ShouldBindUri(&req); err != nil {
		ctx.JSON(http.StatusBadRequest, errorResponse(err))
		return
	}

	if err := ctx.ShouldBindJSON(&req); err != nil {
		ctx.JSON(http.StatusBadRequest, errorResponse(err))
		return
	}
	arg := db.UpdateAccountParams{
		ID:      req.ID,
		Balance: req.Balance,
	}
	updatedAccount, err := server.store.UpdateAccount(ctx, arg)
	if err != nil {
		if err == sql.ErrNoRows {
			ctx.JSON(http.StatusNotFound, errorResponse(err))
			return
		}
		ctx.JSON(http.StatusInternalServerError, errorResponse(err))
		return
	}

	ctx.JSON(http.StatusOK, updatedAccount)
}

TaylorDurden avatar Jul 17 '24 15:07 TaylorDurden

@TaylorDurden thanks for help. However, if I recall correctly, this may not work.

chengr4 avatar Jul 18 '24 00:07 chengr4

@TaylorDurden thanks for help. However, if I recall correctly, this may not work.

@TaylorDurden thanks for help. However, if I recall correctly, this may not work.

https://github.com/TaylorDurden/go-simple-bank/pull/4

You can try it in my PR branch. Maybe the new version gin resolved this.

TaylorDurden avatar Jul 18 '24 04:07 TaylorDurden

@chengr4 I guess that maybe you were testing a 400 Bad Request to the API client without currency field. If you mark the currency field not required, this field won't be validated for 1 struct.

// this required binding will fail ctx.ShouldBindUri validation
Currency string `json:"currency" binding:"required,currency"` 

// this code will validate the struct fileds, if you did not pass the Currency field
ctx.ShouldBindUri(&req)  

FYI: https://github.com/gin-gonic/gin/blob/cc4e11438cd6c0bcc632fe3492d3817dfa21c337/context.go#L763 https://github.com/gin-gonic/gin/blob/cc4e11438cd6c0bcc632fe3492d3817dfa21c337/binding/uri.go#L17

TaylorDurden avatar Jul 21 '24 04:07 TaylorDurden

@TaylorDurden But I usually want to validate it

chengr4 avatar Jul 22 '24 01:07 chengr4

@TaylorDurden But I usually want to validate it

Then you should use another struct for this param...since it is from request body and use ShouldBindJSON to parse & validate it. Although it is not a clean solution but it works as you metioned above...

TaylorDurden avatar Jul 25 '24 12:07 TaylorDurden