diff --git a/HISTORY.md b/HISTORY.md
index f30ab56a..9f1dc903 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -159,11 +159,13 @@ Here is a preview of what the new Hero handlers look like:
 
 Other Improvements:
 
+- New `app.Validator { Struct(interface{}) error }` field and `app.Validate` method were added. The `app.Validator = ` can be used to integrate a 3rd-party package such as [go-playground/validator](https://github.com/go-playground/validator). If set-ed then Iris `Context`'s `ReadJSON`, `ReadXML`, `ReadMsgPack`, `ReadYAML`, `ReadForm`, `ReadQuery`, `ReadBody` methods will return the validation error on data validation failures. The [read-json-struct-validation](_examples/http_request/read-json-struct-validation) example was updated.
+
 - A result of <T> can implement the new `hero.PreflightResult` interface which contains a single method of `Preflight(iris.Context) error`. If this method exists on a custom struct value which is returned from a handler then it will fire that `Preflight` first and if not errored then it will cotninue by sending the struct value as JSON(by-default) response body.
 
 - `ctx.JSON, JSONP, XML`: if `iris.WithOptimizations` is NOT passed on `app.Run/Listen` then the indentation defaults to `"  "` (two spaces) otherwise it is empty or the provided value.
 
-- Hero Handlers (and `app.DI().Handle`) do not have to require `iris.Context` just to call `ctx.Next()` anymore, this is done automatically now. 
+- Hero Handlers (and `app.DI().Handle`) do not have to require `iris.Context` just to call `ctx.Next()` anymore, this is done automatically now.
 
 New Context Methods:
 
diff --git a/_examples/README.md b/_examples/README.md
index 6a83c184..8bd7fbbf 100644
--- a/_examples/README.md
+++ b/_examples/README.md
@@ -246,7 +246,7 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her
 ### How to Read from `context.Request() *http.Request`
 
 - [Read JSON](http_request/read-json/main.go)
-    * [Struct Validation](http_request/read-json-struct-validation/main.go)
+    * [Struct Validation](http_request/read-json-struct-validation/main.go) **UPDaTE**
 - [Read XML](http_request/read-xml/main.go)
 - [Read MsgPack](http_request/read-msgpack/main.go) **NEW**
 - [Read YAML](http_request/read-yaml/main.go)
diff --git a/_examples/http_request/read-json-struct-validation/main.go b/_examples/http_request/read-json-struct-validation/main.go
index b114142c..a1ce1d36 100644
--- a/_examples/http_request/read-json-struct-validation/main.go
+++ b/_examples/http_request/read-json-struct-validation/main.go
@@ -1,103 +1,26 @@
-// Package main shows the validator(latest, version 10) integration with Iris.
-// You can find more examples like this at: https://github.com/go-playground/validator/blob/master/_examples
+// Package main shows the validator(latest, version 10) integration with Iris' Context methods of
+// `ReadJSON`, `ReadXML`, `ReadMsgPack`, `ReadYAML`, `ReadForm`, `ReadQuery`, `ReadBody`.
+//
+// You can find more examples of this 3rd-party library at:
+// https://github.com/go-playground/validator/blob/master/_examples
 package main
 
 import (
 	"fmt"
 
 	"github.com/kataras/iris/v12"
+
 	// $ go get github.com/go-playground/validator/v10
 	"github.com/go-playground/validator/v10"
 )
 
-// User contains user information.
-type User struct {
-	FirstName      string     `json:"fname"`
-	LastName       string     `json:"lname"`
-	Age            uint8      `json:"age" validate:"gte=0,lte=130"`
-	Email          string     `json:"email" validate:"required,email"`
-	FavouriteColor string     `json:"favColor" validate:"hexcolor|rgb|rgba"`
-	Addresses      []*Address `json:"addresses" validate:"required,dive,required"` // a person can have a home and cottage...
-}
-
-// Address houses a users address information.
-type Address struct {
-	Street string `json:"street" validate:"required"`
-	City   string `json:"city" validate:"required"`
-	Planet string `json:"planet" validate:"required"`
-	Phone  string `json:"phone" validate:"required"`
-}
-
-// Use a single instance of Validate, it caches struct info.
-var validate *validator.Validate
-
 func main() {
-	validate = validator.New()
-
-	// Register validation for 'User'
-	// NOTE: only have to register a non-pointer type for 'User', validator
-	// internally dereferences during it's type checks.
-	validate.RegisterStructValidation(UserStructLevelValidation, User{})
-
 	app := iris.New()
-	app.Post("/user", func(ctx iris.Context) {
-		var user User
-		if err := ctx.ReadJSON(&user); err != nil {
-			// Handle error.
-		}
+	app.Validator = validator.New()
 
-		// Returns InvalidValidationError for bad validation input, nil or ValidationErrors ( []FieldError )
-		err := validate.Struct(user)
-		if err != nil {
+	app.Post("/user", postUser)
 
-			// This check is only needed when your code could produce
-			// an invalid value for validation such as interface with nil
-			// value most including myself do not usually have code like this.
-			if _, ok := err.(*validator.InvalidValidationError); ok {
-				ctx.StatusCode(iris.StatusInternalServerError)
-				ctx.WriteString(err.Error())
-				return
-			}
-
-			ctx.StatusCode(iris.StatusBadRequest)
-			for _, err := range err.(validator.ValidationErrors) {
-				fmt.Println()
-				fmt.Println(err.Namespace())
-				fmt.Println(err.Field())
-				fmt.Println(err.StructNamespace()) // Can differ when a custom TagNameFunc is registered or.
-				fmt.Println(err.StructField())     // By passing alt name to ReportError like below.
-				fmt.Println(err.Tag())
-				fmt.Println(err.ActualTag())
-				fmt.Println(err.Kind())
-				fmt.Println(err.Type())
-				fmt.Println(err.Value())
-				fmt.Println(err.Param())
-				fmt.Println()
-
-				// Or collect these as json objects
-				// and send back to the client the collected errors via ctx.JSON
-				// {
-				// 	"namespace":        err.Namespace(),
-				// 	"field":            err.Field(),
-				// 	"struct_namespace": err.StructNamespace(),
-				// 	"struct_field":     err.StructField(),
-				// 	"tag":              err.Tag(),
-				// 	"actual_tag":       err.ActualTag(),
-				// 	"kind":             err.Kind().String(),
-				// 	"type":             err.Type().String(),
-				// 	"value":            fmt.Sprintf("%v", err.Value()),
-				// 	"param":            err.Param(),
-				// }
-			}
-
-			// from here you can create your own error messages in whatever language you wish.
-			return
-		}
-
-		// save user to database.
-	})
-
-	// use Postman or whatever to do a POST request
+	// Use Postman or any tool to perform a POST request
 	// to the http://localhost:8080/user with RAW BODY:
 	/*
 		{
@@ -114,29 +37,95 @@ func main() {
 			}]
 		}
 	*/
-	// Content-Type to application/json (optionally but good practise).
-	// This request will fail due to the empty `User.FirstName` (fname in json)
-	// and `User.LastName` (lname in json).
-	// Check your iris' application terminal output.
-	app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed))
+	/* The response should be:
+	{
+	  "fields": [
+	    {
+	      "tag": "required",
+	      "namespace": "User.FirstName",
+	      "kind": "string",
+	      "type": "string",
+	      "value": "",
+	      "param": ""
+	    },
+	    {
+	      "tag": "required",
+	      "namespace": "User.LastName",
+	      "kind": "string",
+	      "type": "string",
+	      "value": "",
+	      "param": ""
+	    }
+	  ]
+	}
+	*/
+	app.Listen(":8080")
 }
 
-// UserStructLevelValidation contains custom struct level validations that don't always
-// make sense at the field validation level. For Example this function validates that either
-// FirstName or LastName exist; could have done that with a custom field validation but then
-// would have had to add it to both fields duplicating the logic + overhead, this way it's
-// only validated once.
-//
-// NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
-// hooks right into validator and you can combine with validation tags and still have a
-// common error output format.
-func UserStructLevelValidation(sl validator.StructLevel) {
-	user := sl.Current().Interface().(User)
+// User contains user information.
+type User struct {
+	FirstName      string     `json:"fname" validate:"required"`
+	LastName       string     `json:"lname" validate:"required"`
+	Age            uint8      `json:"age" validate:"gte=0,lte=130"`
+	Email          string     `json:"email" validate:"required,email"`
+	FavouriteColor string     `json:"favColor" validate:"hexcolor|rgb|rgba"`
+	Addresses      []*Address `json:"addresses" validate:"required,dive,required"` // a User can have a home and cottage...
+}
 
-	if len(user.FirstName) == 0 && len(user.LastName) == 0 {
-		sl.ReportError(user.FirstName, "FirstName", "fname", "fnameorlname", "")
-		sl.ReportError(user.LastName, "LastName", "lname", "fnameorlname", "")
+// Address houses a users address information.
+type Address struct {
+	Street string `json:"street" validate:"required"`
+	City   string `json:"city" validate:"required"`
+	Planet string `json:"planet" validate:"required"`
+	Phone  string `json:"phone" validate:"required"`
+}
+
+type validationError struct {
+	ActualTag string `json:"tag"`
+	Namespace string `json:"namespace"`
+	Kind      string `json:"kind"`
+	Type      string `json:"type"`
+	Value     string `json:"value"`
+	Param     string `json:"param"`
+}
+
+type errorsResponse struct {
+	ValidationErrors []validationError `json:"fields,omitempty"`
+}
+
+func wrapValidationErrors(errs validator.ValidationErrors) errorsResponse {
+	validationErrors := make([]validationError, 0, len(errs))
+	for _, validationErr := range errs {
+		validationErrors = append(validationErrors, validationError{
+			ActualTag: validationErr.ActualTag(),
+			Namespace: validationErr.Namespace(),
+			Kind:      validationErr.Kind().String(),
+			Type:      validationErr.Type().String(),
+			Value:     fmt.Sprintf("%v", validationErr.Value()),
+			Param:     validationErr.Param(),
+		})
 	}
 
-	// plus can to more, even with different tag than "fnameorlname".
+	return errorsResponse{
+		ValidationErrors: validationErrors,
+	}
+}
+
+func postUser(ctx iris.Context) {
+	var user User
+	err := ctx.ReadJSON(&user)
+	if err != nil {
+		if errs, ok := err.(validator.ValidationErrors); ok {
+			response := wrapValidationErrors(errs)
+			ctx.StatusCode(iris.StatusBadRequest)
+			ctx.JSON(response)
+			return
+		}
+
+		ctx.StatusCode(iris.StatusInternalServerError)
+		ctx.WriteString(err.Error())
+		return
+	}
+
+	ctx.JSON(iris.Map{"message": "OK"})
 }
diff --git a/context/application.go b/context/application.go
index 9ad11227..f0b935f5 100644
--- a/context/application.go
+++ b/context/application.go
@@ -20,6 +20,10 @@ type Application interface {
 	// I18nReadOnly returns the i18n's read-only features.
 	I18nReadOnly() I18nReadOnly
 
+	// Validate validates a value and returns nil if passed or
+	// the failure reason if not.
+	Validate(interface{}) error
+
 	// View executes and write the result of a template file to the writer.
 	//
 	// Use context.View to render templates to the client instead.
diff --git a/context/context.go b/context/context.go
index 026dcd48..03971e21 100644
--- a/context/context.go
+++ b/context/context.go
@@ -2602,6 +2602,15 @@ func GetBody(r *http.Request, resetBody bool) ([]byte, error) {
 	return data, nil
 }
 
+// Validator is the validator for request body on Context methods such as
+// ReadJSON, ReadMsgPack, ReadXML, ReadYAML, ReadForm, ReadQuery, ReadBody and e.t.c.
+type Validator interface {
+	Struct(interface{}) error
+	// If community asks for more than a struct validation on JSON, XML, MsgPack, Form, Query and e.t.c
+	// then we should add more methods here, alternative approach would be to have a
+	// `Validator:Validate(interface{}) error` and a map[reflect.Kind]Validator instead.
+}
+
 // UnmarshalBody reads the request's body and binds it to a value or pointer of any type
 // Examples of usage: context.ReadJSON, context.ReadXML.
 //
@@ -2636,7 +2645,12 @@ func (ctx *context) UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) e
 	// we don't need to reduce the performance here by using the reflect.TypeOf method.
 
 	// f the v doesn't contains a self-body decoder use the custom unmarshaler to bind the body.
-	return unmarshaler.Unmarshal(rawData, outPtr)
+	err = unmarshaler.Unmarshal(rawData, outPtr)
+	if err != nil {
+		return err
+	}
+
+	return ctx.Application().Validate(outPtr)
 }
 
 func (ctx *context) shouldOptimize() bool {
@@ -2687,7 +2701,12 @@ func (ctx *context) ReadForm(formObject interface{}) error {
 		return nil
 	}
 
-	return schema.DecodeForm(values, formObject)
+	err := schema.DecodeForm(values, formObject)
+	if err != nil {
+		return err
+	}
+
+	return ctx.Application().Validate(formObject)
 }
 
 // ReadQuery binds url query to "ptr". The struct field tag is "url".
@@ -2699,7 +2718,12 @@ func (ctx *context) ReadQuery(ptr interface{}) error {
 		return nil
 	}
 
-	return schema.DecodeQuery(values, ptr)
+	err := schema.DecodeQuery(values, ptr)
+	if err != nil {
+		return err
+	}
+
+	return ctx.Application().Validate(ptr)
 }
 
 // ReadProtobuf binds the body to the "ptr" of a proto Message and returns any error.
@@ -2719,7 +2743,12 @@ func (ctx *context) ReadMsgPack(ptr interface{}) error {
 		return err
 	}
 
-	return msgpack.Unmarshal(rawData, ptr)
+	err = msgpack.Unmarshal(rawData, ptr)
+	if err != nil {
+		return err
+	}
+
+	return ctx.Application().Validate(ptr)
 }
 
 // ReadBody binds the request body to the "ptr" depending on the HTTP Method and the Request's Content-Type.
diff --git a/iris.go b/iris.go
index 1fbe87e8..fbf6bb2a 100644
--- a/iris.go
+++ b/iris.go
@@ -155,6 +155,9 @@ type Application struct {
 	// See `Context#Tr` method for request-based translations.
 	I18n *i18n.I18n
 
+	// Validator is the request body validator, defaults to nil.
+	Validator context.Validator
+
 	// view engine
 	view view.View
 	// used for build
@@ -309,6 +312,33 @@ func (app *Application) I18nReadOnly() context.I18nReadOnly {
 	return app.I18n
 }
 
+// Validate validates a value and returns nil if passed or
+// the failure reason if does not.
+func (app *Application) Validate(v interface{}) error {
+	if app.Validator == nil {
+		return nil
+	}
+
+	// val := reflect.ValueOf(v)
+	// if val.Kind() == reflect.Ptr && !val.IsNil() {
+	// 	val = val.Elem()
+	// }
+
+	// if val.Kind() == reflect.Struct && val.Type() != timeType {
+	// 	return app.Validator.Struct(v)
+	// }
+
+	// no need to check the kind, underline lib does it but in the future this may change (look above).
+	err := app.Validator.Struct(v)
+	if err != nil {
+		if !strings.HasPrefix(err.Error(), "validator: ") {
+			return err
+		}
+	}
+
+	return nil
+}
+
 var (
 	// HTML view engine.
 	// Shortcut of the kataras/iris/view.HTML.