Model binding and validation

To bind a request body into a type, use model binding. We currently support binding of JSON, JSONProtobuf, Protobuf, MsgPack, XML, YAML and standard form values (foo=bar&boo=baz).

  1. ReadJSON(outPtr interface{}) error
  2. ReadJSONProtobuf(ptr proto.Message, opts ...ProtoUnmarshalOptions) error
  3. ReadProtobuf(ptr proto.Message) error
  4. ReadMsgPack(ptr interface{}) error
  5. ReadXML(outPtr interface{}) error
  6. ReadYAML(outPtr interface{}) error
  7. ReadForm(formObject interface{}) error
  8. ReadQuery(ptr interface{}) error

When using the ReadBody, Iris tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use the specific ReadXXX methods, e.g. ReadJSON or ReadProtobuf and e.t.c.

  1. ReadBody(ptr interface{}) error

Iris, wisely, not features a builtin data validation. However, it does allow you to attach a validator which will automatically called on methods like ReadJSON, ReadXML…. In this example we will learn how to use the go-playground/validator/v10) for request body validation.

Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set json:"fieldname".

You can also specify that specific fields are required. If a field is decorated with binding:"required" and has a empty value when binding, an error will be returned.

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/kataras/iris/v12"
  5. "github.com/go-playground/validator/v10"
  6. )
  7. func main() {
  8. app := iris.New()
  9. app.Validator = validator.New()
  10. userRouter := app.Party("/user")
  11. {
  12. userRouter.Get("/validation-errors", resolveErrorsDocumentation)
  13. userRouter.Post("/", postUser)
  14. }
  15. app.Listen(":8080")
  16. }
  17. // User contains user information.
  18. type User struct {
  19. FirstName string `json:"fname" validate:"required"`
  20. LastName string `json:"lname" validate:"required"`
  21. Age uint8 `json:"age" validate:"gte=0,lte=130"`
  22. Email string `json:"email" validate:"required,email"`
  23. FavouriteColor string `json:"favColor" validate:"hexcolor|rgb|rgba"`
  24. Addresses []*Address `json:"addresses" validate:"required,dive,required"`
  25. }
  26. // Address houses a users address information.
  27. type Address struct {
  28. Street string `json:"street" validate:"required"`
  29. City string `json:"city" validate:"required"`
  30. Planet string `json:"planet" validate:"required"`
  31. Phone string `json:"phone" validate:"required"`
  32. }
  33. type validationError struct {
  34. ActualTag string `json:"tag"`
  35. Namespace string `json:"namespace"`
  36. Kind string `json:"kind"`
  37. Type string `json:"type"`
  38. Value string `json:"value"`
  39. Param string `json:"param"`
  40. }
  41. func wrapValidationErrors(errs validator.ValidationErrors) []validationError {
  42. validationErrors := make([]validationError, 0, len(errs))
  43. for _, validationErr := range errs {
  44. validationErrors = append(validationErrors, validationError{
  45. ActualTag: validationErr.ActualTag(),
  46. Namespace: validationErr.Namespace(),
  47. Kind: validationErr.Kind().String(),
  48. Type: validationErr.Type().String(),
  49. Value: fmt.Sprintf("%v", validationErr.Value()),
  50. Param: validationErr.Param(),
  51. })
  52. }
  53. return validationErrors
  54. }
  55. func postUser(ctx iris.Context) {
  56. var user User
  57. err := ctx.ReadJSON(&user)
  58. if err != nil {
  59. // Handle the error, below you will find the right way to do that...
  60. if errs, ok := err.(validator.ValidationErrors); ok {
  61. // Wrap the errors with JSON format, the underline library returns the errors as interface.
  62. validationErrors := wrapValidationErrors(errs)
  63. // Fire an application/json+problem response and stop the handlers chain.
  64. ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
  65. Title("Validation error").
  66. Detail("One or more fields failed to be validated").
  67. Type("/user/validation-errors").
  68. Key("errors", validationErrors))
  69. return
  70. }
  71. // It's probably an internal JSON error, let's dont give more info here.
  72. ctx.StopWithStatus(iris.StatusInternalServerError)
  73. return
  74. }
  75. ctx.JSON(iris.Map{"message": "OK"})
  76. }
  77. func resolveErrorsDocumentation(ctx iris.Context) {
  78. ctx.WriteString("A page that should document to web developers or users of the API on how to resolve the validation errors")
  79. }

Sample request

  1. {
  2. "fname": "",
  3. "lname": "",
  4. "age": 45,
  5. "email": "mail@example.com",
  6. "favColor": "#000",
  7. "addresses": [{
  8. "street": "Eavesdown Docks",
  9. "planet": "Persphone",
  10. "phone": "none",
  11. "city": "Unknown"
  12. }]
  13. }

Sample response

  1. {
  2. "title": "Validation error",
  3. "detail": "One or more fields failed to be validated",
  4. "type": "http://localhost:8080/user/validation-errors",
  5. "status": 400,
  6. "fields": [
  7. {
  8. "tag": "required",
  9. "namespace": "User.FirstName",
  10. "kind": "string",
  11. "type": "string",
  12. "value": "",
  13. "param": ""
  14. },
  15. {
  16. "tag": "required",
  17. "namespace": "User.LastName",
  18. "kind": "string",
  19. "type": "string",
  20. "value": "",
  21. "param": ""
  22. }
  23. ]
  24. }

Learn more about model validation at: https://github.com/go-playground/validator/blob/master/_examples