Documentation Index Fetch the complete documentation index at: https://mintlify.com/go-kratos/kratos/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The validation middleware automatically validates incoming requests before they reach your handlers. It supports multiple validation methods including the validator interface, custom validation functions, and protovalidate for Protocol Buffers.
Installation
go get github.com/go-kratos/kratos/v2/middleware/validate
Basic Usage
The Validator function creates a validation middleware:
func Validator ( validators ... ValidatorFunc ) middleware . Middleware
Server Example
import (
" github.com/go-kratos/kratos/v2 "
" github.com/go-kratos/kratos/v2/middleware/validate "
" github.com/go-kratos/kratos/v2/transport/http "
" github.com/go-kratos/kratos/v2/transport/grpc "
)
func main () {
// Create HTTP server with validation
httpSrv := http . NewServer (
http . Address ( ":8000" ),
http . Middleware (
validate . Validator (),
),
)
// Create gRPC server with validation
grpcSrv := grpc . NewServer (
grpc . Address ( ":9000" ),
grpc . Middleware (
validate . Validator (),
),
)
app := kratos . New (
kratos . Server ( httpSrv , grpcSrv ),
)
if err := app . Run (); err != nil {
log . Fatal ( err )
}
}
Validation Methods
1. Validator Interface
Implement the Validate() method on your request types:
type validator interface {
Validate () error
}
Usage Example
import (
" errors "
" strings "
)
type CreateUserRequest struct {
Name string
Email string
Age int32
}
// Implement Validate method
func ( r * CreateUserRequest ) Validate () error {
if r . Name == "" {
return errors . New ( "name is required" )
}
if len ( r . Name ) < 2 {
return errors . New ( "name must be at least 2 characters" )
}
if r . Email == "" {
return errors . New ( "email is required" )
}
if ! strings . Contains ( r . Email , "@" ) {
return errors . New ( "invalid email format" )
}
if r . Age < 0 || r . Age > 150 {
return errors . New ( "age must be between 0 and 150" )
}
return nil
}
The middleware automatically calls Validate() on any request that implements the interface.
2. Custom Validator Functions
Pass custom validation functions to the middleware:
type ValidatorFunc func ( v any ) error
Usage Example
import (
" github.com/go-kratos/kratos/v2/middleware/validate "
)
// Custom validator function
func customValidator ( v any ) error {
// Type assert to your request type
switch req := v .( type ) {
case * CreateUserRequest :
if req . Name == "admin" {
return errors . New ( "cannot use reserved name" )
}
}
return nil
}
// Use multiple validators
validate . Validator (
customValidator ,
anotherValidator ,
)
3. Protocol Buffers Validation
For Protocol Buffers, use protovalidate or protoc-gen-validate:
Using buf protovalidate
go get buf.build/go/protovalidate
go get github.com/go-kratos/contrib/middleware/validate
import (
validate " github.com/go-kratos/contrib/middleware/validate "
" github.com/go-kratos/kratos/v2/transport/http "
)
// Use protovalidate middleware
httpSrv := http . NewServer (
http . Address ( ":8000" ),
http . Middleware (
validate . ProtoValidate (),
),
)
Proto definition with validation rules:
syntax = "proto3" ;
import "buf/validate/validate.proto" ;
message CreateUserRequest {
string name = 1 [ (buf.validate.field).string = {
min_len : 2 ,
max_len : 50
}];
string email = 2 [ (buf.validate.field) .string .email = true ];
int32 age = 3 [ (buf.validate.field).int32 = {
gte : 0 ,
lte : 150
}];
}
Using Google AIP Field Behavior
import (
" google.golang.org/protobuf/proto "
" go.einride.tech/aip/fieldbehavior "
" github.com/go-kratos/kratos/v2/middleware/validate "
)
// Validator for required fields
func aipValidator ( v any ) error {
if msg , ok := v .( proto . Message ); ok {
if err := fieldbehavior . ValidateRequiredFields ( msg ); err != nil {
return err
}
}
return nil
}
validate . Validator ( aipValidator )
Proto definition:
syntax = "proto3" ;
import "google/api/field_behavior.proto" ;
message CreateUserRequest {
string name = 1 [ (google.api.field_behavior) = REQUIRED ];
string email = 2 [ (google.api.field_behavior) = REQUIRED ];
int32 age = 3 [ (google.api.field_behavior) = OPTIONAL ];
}
Error Handling
Validation errors are returned as:
errors . BadRequest ( "VALIDATOR" , err . Error ()). WithCause ( err )
This returns:
HTTP status: 400 Bad Request
gRPC code: INVALID_ARGUMENT
Reason: "VALIDATOR"
Error Response Example
{
"code" : 400 ,
"reason" : "VALIDATOR" ,
"message" : "name is required" ,
"metadata" : {}
}
Validation Order
The middleware validates in this order:
Built-in Validate() method (if request implements validator interface)
Custom validator functions (in the order provided)
If any validation fails, subsequent validators are not executed.
Complete Example
package main
import (
" context "
" errors "
" log "
" strings "
" github.com/go-kratos/kratos/v2 "
kratosErrors " github.com/go-kratos/kratos/v2/errors "
" github.com/go-kratos/kratos/v2/middleware/validate "
" github.com/go-kratos/kratos/v2/transport/http "
)
// Request type with validation
type CreateUserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
Age int32 `json:"age"`
Role string `json:"role"`
}
// Implement validator interface
func ( r * CreateUserRequest ) Validate () error {
if r . Name == "" {
return errors . New ( "name is required" )
}
if len ( r . Name ) < 2 {
return errors . New ( "name must be at least 2 characters" )
}
if r . Email == "" {
return errors . New ( "email is required" )
}
if ! strings . Contains ( r . Email , "@" ) {
return errors . New ( "invalid email format" )
}
if r . Age < 0 || r . Age > 150 {
return errors . New ( "age must be between 0 and 150" )
}
return nil
}
// Custom business logic validator
func businessRuleValidator ( v any ) error {
switch req := v .( type ) {
case * CreateUserRequest :
// Check reserved names
reservedNames := [] string { "admin" , "root" , "system" }
for _ , reserved := range reservedNames {
if strings . EqualFold ( req . Name , reserved ) {
return errors . New ( "name is reserved and cannot be used" )
}
}
// Validate role
validRoles := [] string { "user" , "moderator" , "admin" }
roleValid := false
for _ , role := range validRoles {
if req . Role == role {
roleValid = true
break
}
}
if ! roleValid {
return errors . New ( "invalid role" )
}
}
return nil
}
// Service implementation
type UserService struct {}
func ( s * UserService ) CreateUser ( ctx context . Context , req * CreateUserRequest ) error {
// Validation is already done by middleware
log . Printf ( "Creating user: %s ( %s ) \n " , req . Name , req . Email )
return nil
}
func main () {
// Create HTTP server with validation
httpSrv := http . NewServer (
http . Address ( ":8000" ),
http . Middleware (
validate . Validator (
businessRuleValidator ,
),
),
)
app := kratos . New (
kratos . Name ( "validation-example" ),
kratos . Server ( httpSrv ),
)
if err := app . Run (); err != nil {
log . Fatal ( err )
}
}
Testing Validation
package main
import (
" context "
" testing "
" github.com/go-kratos/kratos/v2/middleware/validate "
)
func TestRequestValidation ( t * testing . T ) {
tests := [] struct {
name string
req * CreateUserRequest
wantErr bool
}{
{
name : "valid request" ,
req : & CreateUserRequest {
Name : "John Doe" ,
Email : "john@example.com" ,
Age : 30 ,
},
wantErr : false ,
},
{
name : "missing name" ,
req : & CreateUserRequest {
Email : "john@example.com" ,
Age : 30 ,
},
wantErr : true ,
},
{
name : "invalid email" ,
req : & CreateUserRequest {
Name : "John Doe" ,
Email : "invalid-email" ,
Age : 30 ,
},
wantErr : true ,
},
{
name : "invalid age" ,
req : & CreateUserRequest {
Name : "John Doe" ,
Email : "john@example.com" ,
Age : 200 ,
},
wantErr : true ,
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
err := tt . req . Validate ()
if ( err != nil ) != tt . wantErr {
t . Errorf ( "Validate() error = %v , wantErr %v " , err , tt . wantErr )
}
})
}
}
Advanced Validation
Conditional Validation
func ( r * CreateUserRequest ) Validate () error {
// Basic validation
if r . Name == "" {
return errors . New ( "name is required" )
}
// Conditional validation
if r . Role == "admin" {
if r . Age < 18 {
return errors . New ( "admin users must be 18 or older" )
}
}
return nil
}
Cross-Field Validation
type DateRange struct {
StartDate time . Time
EndDate time . Time
}
func ( r * DateRange ) Validate () error {
if r . StartDate . IsZero () {
return errors . New ( "start_date is required" )
}
if r . EndDate . IsZero () {
return errors . New ( "end_date is required" )
}
if r . EndDate . Before ( r . StartDate ) {
return errors . New ( "end_date must be after start_date" )
}
return nil
}
Using Third-Party Validators
go-playground/validator
go get github.com/go-playground/validator/v10
import (
" github.com/go-playground/validator/v10 "
" github.com/go-kratos/kratos/v2/middleware/validate "
)
var v = validator . New ()
// Validator function
func goPlaygroundValidator ( req any ) error {
return v . Struct ( req )
}
// Request with tags
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=2,max=50"`
Email string `json:"email" validate:"required,email"`
Age int32 `json:"age" validate:"gte=0,lte=150"`
}
// Use in middleware
validate . Validator ( goPlaygroundValidator )
Best Practices
Always place validation middleware early in the chain, after recovery but before business logic. http . Middleware (
recovery . Recovery (),
validate . Validator (),
logging . Server ( logger ),
// ... business middleware
)
Validation should check format and constraints, not business logic. Use separate middleware for business rules. // Good: Format validation
if ! emailRegex . Match ( email ) {
return errors . New ( "invalid email format" )
}
// Bad: Business logic in validation
if userExists ( email ) {
return errors . New ( "user already exists" )
}
Return Clear Error Messages
Provide specific, actionable error messages that help users fix their requests. // Good
return errors . New ( "age must be between 0 and 150" )
// Bad
return errors . New ( "invalid age" )
Use protovalidate for Proto
For Protocol Buffers, use protovalidate to define validation rules in .proto files.
Test Validation Thoroughly
Write comprehensive tests for all validation rules including edge cases.
Don't Duplicate Validation
Implement validation once in the middleware, not in every handler.
Selective Validation
Apply validation to specific routes:
import (
" github.com/go-kratos/kratos/v2/middleware/validate "
" github.com/go-kratos/kratos/v2/middleware/selector "
)
// Apply validation only to write operations
http . Middleware (
selector . Server (
validate . Validator (),
). Match ( func ( ctx context . Context , operation string ) bool {
// Validate POST, PUT, PATCH
return strings . Contains ( operation , "Create" ) ||
strings . Contains ( operation , "Update" ) ||
strings . Contains ( operation , "Delete" )
}). Build (),
)
Common Validation Rules
Here are common validation patterns:
func ( r * Request ) Validate () error {
// Required field
if r . Field == "" {
return errors . New ( "field is required" )
}
// Length constraints
if len ( r . Field ) < 2 || len ( r . Field ) > 50 {
return errors . New ( "field must be 2-50 characters" )
}
// Numeric range
if r . Number < 0 || r . Number > 100 {
return errors . New ( "number must be 0-100" )
}
// Regex pattern
if ! regexp . MustCompile ( `^[a-zA-Z]+$` ). MatchString ( r . Field ) {
return errors . New ( "field must contain only letters" )
}
// Email format
if ! strings . Contains ( r . Email , "@" ) || ! strings . Contains ( r . Email , "." ) {
return errors . New ( "invalid email format" )
}
// URL format
if _ , err := url . Parse ( r . URL ); err != nil {
return errors . New ( "invalid URL format" )
}
// Enum validation
validValues := map [ string ] bool { "option1" : true , "option2" : true }
if ! validValues [ r . Field ] {
return errors . New ( "invalid value" )
}
return nil
}
Source Reference
The validation middleware implementation can be found in:
middleware/validate/validate.go:44 - Validator middleware
middleware/validate/validate.go:10 - ValidatorFunc type
middleware/validate/validate.go:13 - validator interface
contrib/middleware/validate/validate.go:18 - ProtoValidate middleware
Next Steps
Authentication Add JWT authentication
Logging Log validation failures