3

I am trying to ensure the body of a post request for example contains exact structure of the body and if not ahould throw an error

for example i have the following function

func UpdatePassword(c *fiber.Ctx) error {

    type UpdatePasswordData struct {
        Password  string `json:"password" form:"password"`
        NewPassword string `json:"new_password" form:"new_password"`
        NewPasswordConfirm string `json:"new_password_confirm" form:"new_password_confirm"`
    }
    
    data := UpdatePasswordData{}

    if err := c.BodyParser(&data); err != nil {
        return err
    }

    var user models.User
    
    if data.NewPassword != data.NewPasswordConfirm {
        c.Status(400)
        return c.JSON(fiber.Map{
            "message": "passwords do not match",
        })
    }

    email, _ := middlewares.GetUserEmail(c)

    newPassword := models.HashPassword(data.NewPassword)

    database.DB.Model(&user).Select("Password").Where("email = ?", email).Updates(map[string]interface{}{"Password": newPassword})

    return c.JSON(user)
}

the POST request should be looking for body with this structure

{
    "password": "oldpassword",
    "new_password": "newpassword",
    "new_password_confirm": "newpassword",
}

but currently this endpoint accepts body that does not have this exact structure. So how do i enforce the structure in the body of request, so that if structure does not match, i throw an error?

uberrebu
  • 3,597
  • 9
  • 38
  • 73
  • 1
    You can use a JSON schema to validate the body, and then unmarshal yourself if validation is successful. – Burak Serdar Jan 04 '22 at 04:50
  • 2
    I'm proud to say I wrote a library that takes care of this exact issue: https://github.com/Kangaroux/go-map-schema. EDIT: By "exact structure" I'm not sure if you want to throw if there are extra fields. That is not something the `go-map-schema` lib currently checks for, it only looks for the fields on the struct. – Jessie Jan 04 '22 at 04:53
  • @BurakSerdar mind posting an answer? – uberrebu Jan 04 '22 at 04:54
  • @Jesse as long as structure of POST body does not match what is expected, will like to throw an error – uberrebu Jan 04 '22 at 05:00
  • https://pkg.go.dev/encoding/json#Decoder.DisallowUnknownFields Create a custom decoder and enable this flag to throw an error. [Decoder example](https://pkg.go.dev/encoding/json#example-Decoder) – Jessie Jan 04 '22 at 05:02
  • you can use JSON schema validator like https://pkg.go.dev/gopkg.in/go-playground/validator.v8. – shubham_asati Jan 04 '22 at 05:04
  • @shubham_asati mind posting an answer using https://pkg.go.dev/github.com/go-playground/validator? – uberrebu Jan 04 '22 at 05:21
  • https://pkg.go.dev/github.com/gorilla/schema –  Jan 04 '22 at 14:05
  • https://json-schema.org/implementations.html#validator-go –  Jan 04 '22 at 14:06
  • anyone knows if there a way to use validator to convert any strings from react frontend POST data to proper data types like float64? react frontend is bringing everything as string – uberrebu Jan 17 '22 at 06:47

2 Answers2

5

do not like gin, fiber has not builtin validate package

use go-playground/validator

go get github.com/go-playground/validator

example

type UpdatePasswordData struct {
        Password  string `json:"password" validate:"required,min=8,max=32"`
        NewPassword string `json:"new_password" validate:"required,min=8,max=32"`
        NewPasswordConfirm string `json:"new_password_confirm" validate:"eqfield=NewPassword"`
}

func UpdatePassword(c *fiber.Ctx) error {
  var body UpdatePasswordData
  if err := c.BodyParser(&body); err != nil {
    return err
  }

  validate := validator.New()
  if err := validate.Struct(body); err != nil {
    return err
  }

  // do others
  // get current user, check password == hash(body.password)
  // save new passworld
}

or you can see fiber office docs https://docs.gofiber.io/guide/validation#validator-package

Goro
  • 66
  • 1
  • i see you took the `UpdatePasswordData` struct outside of the function, any reason for that? the function is really the only one that will use that struct – uberrebu Jan 04 '22 at 19:46
  • is there a way to use validator to convert any strings from react to proper data types like float64? react frontend is bringing everything as string – uberrebu Jan 17 '22 at 06:45
0

We can use struct tag

`validate:"required"`

to ensure that all the mandatory fields are there in the request payload.

Moreover we can validate the fields with the provided tags of the validator package and for additional validations we can implement custom validators and register them like this:

validate := validator.New()
validate.RegisterValidation("password-validator", PasswordValidator)