GoravelGoravel
Home
Video
  • English
  • 简体中文
GitHub
Home
Video
  • English
  • 简体中文
GitHub
  • Prologue

    • Upgrade Guide

      • Upgrading To v1.15 From v1.14
      • Upgrading To v1.14 From v1.13
      • History Upgrade
    • Contribution Guide
    • Excellent Extend Packages
  • Getting Started

    • Installation
    • Configuration
    • Directory Structure
    • Compile
  • Architecture Concepts

    • Request Lifecycle
    • Service Container
    • Service Providers
    • Facades
  • The Basics

    • Routing
    • HTTP Middleware
    • Controllers
    • Requests
    • Responses
    • Views
    • Grpc
    • Session
    • Validation
    • Logging
  • Digging Deeper

    • Artisan Console
    • Cache
    • Events
    • File Storage
    • Mail
    • Queues
    • Task Scheduling
    • Localization
    • Package Development
    • Color
    • Strings
    • Helpers
  • Security

    • Authentication
    • Authorization
    • Encryption
    • Hashing
  • ORM

    • Getting Started
    • Relationships
    • Migrations
    • Seeding
    • Factories
  • Testing

    • Getting Started
    • HTTP Tests
    • Mock

Validation

  • Introduction
  • Validation Quickstart
    • Defining The Routes
    • Creating The Controller
    • Writing The Validation Logic
    • Nested Attributes
    • Slice Validation
  • Form Request Validation
    • Creating Form Requests
    • Authorizing Form Requests
    • Filter Input Data
    • Customizing The Error Messages
    • Customizing The Validation Attributes
    • Preparing Input For Validation
  • Manually Creating Validators
    • Customizing The Error Messages
    • Specifying A Custom Message For A Given Attribute
    • Specifying Custom Attribute Values
    • Format Data Before Validation
  • Working With Validated Input
  • Working With Error Messages
    • Retrieving one Error Message For A Field (Random)
    • Retrieving All Error Messages For A Field
    • Retrieving All Error Messages For All Fields
    • Determining If Error Messages Exist For A Field
  • Available Validation Rules
    • Points For Using Rules
  • Custom Validation Rules
  • Available Validation Filters
  • Custom filter

Introduction

Goravel provides several different approaches to validate your application's incoming data. It is most common to use the Validate method available on all incoming HTTP requests. Goravel includes a wide variety of convenient validation rules.

Validation Quickstart

Let's take a closer look at a complete example of how to validate a form and return error messages to the user. This overview will provide you with a general understanding of how to validate incoming request data using Goravel.

Defining The Routes

First, let's assume we have the following routes defined in our routes/web.go file:

import "goravel/app/http/controllers"

postController := controllers.NewPostController()
facades.Route().Get("/post/create", postController.Create)
facades.Route().Post("/post", postController.Store)

The GET route displays a form for creating a new blog post. The POST route stores the new post in the database.

Creating The Controller

Next, let's take a look at a simple controller that handles incoming requests to these routes. We'll leave the Store method empty for now:

package controllers

import (
  "github.com/goravel/framework/contracts/http"
)

type PostController struct {
  // Dependent services
}

func NewPostController() *PostController {
  return &PostController{
    // Inject services
  }
}

func (r *PostController) Create(ctx http.Context) {

}

func (r *PostController) Store(ctx http.Context) {

}

Writing The Validation Logic

Now we are ready to fill in our Store method with the logic to validate the new blog post.

func (r *PostController) Store(ctx http.Context) {
  validator, err := ctx.Request().Validate(map[string]string{
    "title": "required|max_len:255",
    "body": "required",
    "code": "required|regex:^\d{4,6}$",
  })
}

Nested Attributes

If the incoming HTTP request contains "nested" field data, you may specify these fields in your validation rules using the "dot" syntax:

validator, err := ctx.Request().Validate(map[string]string{
  "title": "required|max_len:255",
  "author.name": "required",
  "author.description": "required",
})

Slice Validation

If the incoming HTTP request contains "array" field data, you may specify these fields in your validation rules using the * syntax:

validator, err := ctx.Request().Validate(map[string]string{
  "tags.*": "required",
})

Form Request Validation

Creating Form Requests

For more complex validation scenarios, you may wish to create a "form request". Form requests are custom request classes that encapsulate their own validation and authorization logic. To create a form request class, you may use the make:request Artisan CLI command:

go run . artisan make:request StorePostRequest
go run . artisan make:request user/StorePostRequest

The generated form request class will be placed in the app/http/requests directory. If this directory does not exist, it will be created when you run the make:request command. Each form request generated by Goravel has six methods: Authorize, Rules. In addition, you can customize the Filters, Messages, Attributes and PrepareForValidation methods for further operations.

The Authorize method is responsible for determining if the currently authenticated user can perform the action represented by the request, while the Rules method returns the validation rules that should apply to the request's data:

package requests

import (
  "github.com/goravel/framework/contracts/http"
  "github.com/goravel/framework/contracts/validation"
)

type StorePostRequest struct {
  Name string `form:"name" json:"name"`
}

func (r *StorePostRequest) Authorize(ctx http.Context) error {
  return nil
}

func (r *StorePostRequest) Rules(ctx http.Context) map[string]string {
  return map[string]string{
    // The keys are consistent with the incoming keys.
    "name": "required|max_len:255",
  }
}

func (r *StorePostRequest) Filters(ctx http.Context) map[string]string {
  return map[string]string{
    "name": "trim",
  }
}

func (r *StorePostRequest) Messages(ctx http.Context) map[string]string {
  return map[string]string{}
}

func (r *StorePostRequest) Attributes(ctx http.Context) map[string]string {
  return map[string]string{}
}

func (r *StorePostRequest) PrepareForValidation(ctx http.Context, data validation.Data) error {
  return nil
}

So, how are the validation rules evaluated? All you need to do is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic:

Then you can use the ValidateRequest method to validate the request in the controller:

func (r *PostController) Store(ctx http.Context) {
  var storePost requests.StorePostRequest
  errors, err := ctx.Request().ValidateRequest(&storePost)
}

Check more rules in the Available Validation Rules section.

Note that since form passed values ​​are of string type by default, all fields in request should also be of string type, otherwise please use JSON to pass values.

Authorizing Form Requests

The form request class also contains an Authorize method. Within this method, you may determine if the authenticated user actually has the authority to update a given resource. For example, you may determine if a user actually owns a blog comment they are attempting to update. Most likely, you will interact with your authorization gates and policies within this method:

func (r *StorePostRequest) Authorize(ctx http.Context) error {
  var comment models.Comment
  facades.Orm().Query().First(&comment)
  if comment.ID == 0 {
    return errors.New("no comment is found")
  }

  if !facades.Gate().Allows("update", map[string]any{
    "comment": comment,
  }) {
    return errors.New("can't update comment")
  }

  return nil
}

error will be passed to the return value of ctx.Request().ValidateRequest.

Filter Input Data

You can format the input data by improving the Filters method of the form request. This method should return an map of attribute/filter:

func (r *StorePostRequest) Filters(ctx http.Context) map[string]string {
  return map[string]string{
    "name": "trim",
  }
}

Customizing The Error Messages

You may customize the error messages used by the form request by overriding the Messages method. This method should return an array of attribute / rule pairs and their corresponding error messages:

func (r *StorePostRequest) Messages() map[string]string {
  return map[string]string{
    "title.required": "A title is required",
    "body.required": "A message is required",
  }
}

Customizing The Validation Attributes

Many of Goravel's built-in validation rule error messages contain an :attribute placeholder. If you would like the :attribute placeholder of your validation message to be replaced with a custom attribute name, you may specify the custom names by overriding the Attributes method. This method should return an array of attribute / name pairs:

func (r *StorePostRequest) Attributes() map[string]string {
  return map[string]string{
    "email": "email address",
  }
}

Preparing Input For Validation

If you need to prepare or sanitize any data from the request before you apply your validation rules, you may use the PrepareForValidation method:

func (r *StorePostRequest) PrepareForValidation(ctx http.Context, data validation.Data) error {
  if name, exist := data.Get("name"); exist {
    return data.Set("name", name.(string)+"1")
  }
  return nil
}

Manually Creating Validators

If you do not want to use the Validate method on the request, you may create a validator instance manually using the facades.Validator. The Make method of the facade generates a new validator instance:

func (r *PostController) Store(ctx http.Context) http.Response {
  validator, _ := facades.Validation().Make(
    map[string]any{
      "name": "Goravel",
    },
    map[string]string{
      "title": "required|max_len:255",
      "body":  "required",
    })

  if validator.Fails() {
    // Return fail
  }

  var user models.User
  err := validator.Bind(&user)
  ...
}

The first argument passed to the Make method is the data under validation which can be map[string]any or struct. The second argument is an array of validation rules to be applied to the data.

Customizing The Error Messages

If needed, you may provide custom error messages that a validator instance should use instead of the default error messages provided by Goravel. You may pass the custom messages as the third argument to the Make method (also applicable to ctx.Request().Validate()):

validator, err := facades.Validation().Make(input, rules, validation.Messages(map[string]string{
  "required": "The :attribute field is required.",
}))

Specifying A Custom Message For A Given Attribute

Sometimes you may wish to specify a custom error message only for a specific attribute. You may do so using "dot" notation. Specify the attribute's name first, followed by the rule (also applicable to ctx.Request().Validate()):

validator, err := facades.Validation().Make(input, rules, validation.Messages(map[string]string{
  "email.required": "We need to know your email address!",
}))

Specifying Custom Attribute Values

Many of Goravel's built-in error messages include an :attribute placeholder that is replaced with the name of the field or attribute under validation. To customize the values used to replace these placeholders for specific fields, you may pass an array of custom attributes as the third argument to the Make method (also applicable to ctx.Request().Validate()):

validator, err := facades.Validation().Make(input, rules, validation.Attributes(map[string]string{
  "email": "email address",
}))

Format Data Before Validation

You can format the data before validating the data for more flexible data validation, and you can pass the method of formatting the data as the third parameter to the Make method (also applicable to ctx.Request().Validate()):

import (
  validationcontract "github.com/goravel/framework/contracts/validation"
  "github.com/goravel/framework/validation"
)

func (r *PostController) Store(ctx http.Context) http.Response {
  validator, err := facades.Validation().Make(input, rules,
    validation.PrepareForValidation(func(ctx http.Context, data validationcontract.Data) error {
      if name, exist := data.Get("name"); exist {
        return data.Set("name", name)
      }

      return nil
    }))

  ...
}

Working With Validated Input

After validating incoming request data using form requests or manually created validator instances, you still want to bind the request data to a struct, there are two ways to do this:

  1. Use the Bind method, this will bind all incoming data, including unvalidated data:
validator, err := ctx.Request().Validate(rules)
var user models.User
err := validator.Bind(&user)

validator, err := facades.Validation().Make(input, rules)
var user models.User
err := validator.Bind(&user)
  1. The incoming data is automatically bound to the form when you use request for validation:
var storePost requests.StorePostRequest
errors, err := ctx.Request().ValidateRequest(&storePost)
fmt.Println(storePost.Name)

Working With Error Messages

Retrieving one Error Message For A Field (Random)

validator, err := ctx.Request().Validate(rules)
validator, err := facades.Validation().Make(input, rules)

message := validator.Errors().One("email")

Retrieving All Error Messages For A Field

messages := validator.Errors().Get("email")

Retrieving All Error Messages For All Fields

messages := validator.Errors().All()

Determining If Error Messages Exist For A Field

if validator.Errors().Has("email") {
  //
}

Available Validation Rules

Below is a list of all available validation rules and their function:

NameDescription
requiredCheck value is required and cannot be zero value. For example, field type is bool, the passing value is false, it can not pass the validation.
required_ifrequired_if:anotherfield,value,... The field under validation must be present and not empty if the anotherField field is equal to any value.
required_unlessrequired_unless:anotherfield,value,... The field under validation must be present and not empty unless the anotherField field is equal to any value.
required_withrequired_with:foo,bar,... The field under validation must be present and not empty only if any of the other specified fields are present.
required_with_allrequired_with_all:foo,bar,... The field under validation must be present and not empty only if all of the other specified fields are present.
required_withoutrequired_without:foo,bar,... The field under validation must be present and not empty only when any of the other specified fields are not present.
required_without_allrequired_without_all:foo,bar,... The field under validation must be present and not empty only when all of the other specified fields are not present.
intCheck value is intX uintX type, and support size checking. eg: int int:2 int:2,12. Notice: Points for using rules
uintCheck value is uint(uintX) type, value >= 0
boolCheck value is bool string(true: "1", "on", "yes", "true", false: "0", "off", "no", "false").
stringCheck value is string type, and support size checking. eg:string string:2 string:2,12
floatCheck value is float(floatX) type
sliceCheck value is slice type([]intX []uintX []byte []string)
inin:foo,bar,… Check if the value is in the given enumeration
not_innot_in:foo,bar,… Check if the value is not in the given enumeration
starts_withstarts_with:foo Check if the input string value is starts with the given sub-string
ends_withends_with:foo Check if the input string value is ends with the given sub-string
betweenbetween:min,max Check that the value is a number and is within the given range
maxmax:value Check value is less than or equal to the given value(intX uintX floatX)
minmin:value Check value is greater than or equal to the given value(intX uintX floatX)
eqeq:value Check that the input value is equal to the given value
nene:value Check that the input value is not equal to the given value
ltlt:value Check value is less than the given value(intX uintX floatX)
gtgt:value Check value is greater than the given value(intX uintX floatX)
lenlen:value Check value length is equals to the given size(string array slice map)
min_lenmin_len:value Check the minimum length of the value is the given size(string array slice map)
max_lenmax_len:value Check the maximum length of the value is the given size(string array slice map)
emailCheck value is email address string
arrayCheck value is array, slice type
mapCheck value is a MAP type
eq_fieldeq_field:field Check that the field value is equals to the value of another field
ne_fieldne_field:field Check that the field value is not equals to the value of another field
gt_fieldgt_field:field Check that the field value is greater than the value of another field
gte_fieldgte_field:field Check that the field value is greater than or equal to the value of another field
lt_fieldlt_field:field Check that the field value is less than the value of another field
lte_fieldlte_field:field Check if the field value is less than or equal to the value of another field
fileVerify if it is an uploaded file
imageCheck if it is an uploaded image file and support suffix check
dateCheck the field value is date string
gt_dategt_date:value Check that the input value is greater than the given date string
lt_datelt_date:value Check that the input value is less than the given date string
gte_dategte_date:value Check that the input value is greater than or equal to the given date string
lte_datelte_date:value Check that the input value is less than or equal to the given date string
alphaVerify that the value contains only alphabetic characters
alpha_numCheck that only letters, numbers are included
alpha_dashCheck to include only letters, numbers, dashes ( - ), and underscores ( _ )
jsonCheck value is JSON string
numberCheck value is number string >= 0
full_urlCheck value is full URL string(must start with http,https)
ipCheck value is IP(v4 or v6) string
ipv4Check value is IPv4 string
ipv6Check value is IPv6 string
regexCheck if the value can pass the regular verification

Points For Using Rules

int

When using ctx.Request().Validate(rules) for validation, the incoming int type data will be parsed by json.Unmarshal into float64 type, which will cause the int rule validation to fail.

Solutions

Option 1: Add validation.PrepareForValidation, format the data before validating the data;

Option 2: Use facades.Validation().Make() for rule validation;

Custom Validation Rules

Goravel provides a variety of helpful validation rules; however, you may wish to specify some of your own. One method of registering custom validation rules is using rule objects. To generate a new rule object, you can simply use the make:rule Artisan command.

For instance, if you want to verify that a string is uppercase, you can create a rule with this command. Goravel will then save this new rule in the app/rules directory. If this directory does not exist, Goravel will create it when you run the Artisan command to create your rule.

go run . artisan make:rule Uppercase
go run . artisan make:rule user/Uppercase

After creating the rule, we need to define its behavior. A rule object has two methods: Passes and Message. The Passes method receives all data, including the data to be validated and the validation parameters. It should return true or false depending on whether the attribute value is valid. The Message method should return the error message for validation that should be used when the validation fails.

package rules

import (
  "strings"

  "github.com/goravel/framework/contracts/validation"
)

type Uppercase struct {
}

// Signature The name of the rule.
func (receiver *Uppercase) Signature() string {
  return "uppercase"
}

// Passes Determine if the validation rule passes.
func (receiver *Uppercase) Passes(data validation.Data, val any, options ...any) bool {
  return strings.ToUpper(val.(string)) == val.(string)
}

// Message Get the validation error message.
func (receiver *Uppercase) Message() string {
  return "The :attribute must be uppercase."
}

Then you need to register the rule to the rules method in the app/providers/validation_service_provider.go file, and the rule can be used like other rules:

package providers

import (
  "github.com/goravel/framework/contracts/validation"
  "github.com/goravel/framework/facades"

  "goravel/app/rules"
)

type ValidationServiceProvider struct {
}

func (receiver *ValidationServiceProvider) Register() {

}

func (receiver *ValidationServiceProvider) Boot() {
  if err := facades.Validation().AddRules(receiver.rules()); err != nil {
    facades.Log().Errorf("add rules error: %+v", err)
  }
}

func (receiver *ValidationServiceProvider) rules() []validation.Rule {
  return []validation.Rule{
    &rules.Uppercase{},
  }
}

Available Validation Filters

NameDescription
int/toIntConvert value(string/intX/floatX) to int type v.FilterRule("id", "int")
uint/toUintConvert value(string/intX/floatX) to uint type v.FilterRule("id", "uint")
int64/toInt64Convert value(string/intX/floatX) to int64 type v.FilterRule("id", "int64")
float/toFloatConvert value(string/intX/floatX) to float type
bool/toBoolConvert string value to bool. (true: "1", "on", "yes", "true", false: "0", "off", "no", "false")
trim/trimSpaceClean up whitespace characters on both sides of the string
ltrim/trimLeftClean up whitespace characters on left sides of the string
rtrim/trimRightClean up whitespace characters on right sides of the string
int/integerConvert value(string/intX/floatX) to int type v.FilterRule("id", "int")
lower/lowercaseConvert string to lowercase
upper/uppercaseConvert string to uppercase
lcFirst/lowerFirstConvert the first character of a string to lowercase
ucFirst/upperFirstConvert the first character of a string to uppercase
ucWord/upperWordConvert the first character of each word to uppercase
camel/camelCaseConvert string to camel naming style
snake/snakeCaseConvert string to snake naming style
escapeJs/escapeJSEscape JS string.
escapeHtml/escapeHTMLEscape HTML string.
str2ints/strToIntsConvert string to int slice []int
str2time/strToTimeConvert date string to time.Time.
str2arr/str2array/strToArrayConvert string to string slice []string

Custom filter

Goravel provides a variety of helpful filters, however, you may wish to specify some of your own. To generate a new rule object, you can simply use the make:filter Artisan command. Let's use this command to generate a rule that converts a string to an integer. This rule is already built into the framework, we just create it as an example. Goravel will save this new filter in the app/filters directory. If this directory does not exist, Goravel will create it when you run the Artisan command to create the rule:

go run . artisan make:filter ToInt
// or
go run . artisan make:filter user/ToInt

One filter contains two methods: Signature and Handle. The Signature method sets the name of the filter. The Handle method performs the specific filtering logic:

package filters

import (
  "strings"

  "github.com/spf13/cast"
  "github.com/goravel/framework/contracts/validation"
)

type ToInt struct {
}

// Signature The signature of the filter.
func (receiver *ToInt) Signature() string {
  return "ToInt"
}

// Handle defines the filter function to apply.
func (receiver *ToInt) Handle() any {
  return func (val any) int {
    return cast.ToString(val)
  }
}

Then you need to register the filter to the filters method in the app/providers/validation_service_provider.go file, and the filter can be used like others:

package providers

import (
  "github.com/goravel/framework/contracts/validation"
  "github.com/goravel/framework/facades"

  "goravel/app/filters"
)

type ValidationServiceProvider struct {
}

func (receiver *ValidationServiceProvider) Register() {

}

func (receiver *ValidationServiceProvider) Boot() {
  if err := facades.Validation().AddFilters(receiver.filters()); err != nil {
    facades.Log().Errorf("add filters error: %+v", err)
  }
}

func (receiver *ValidationServiceProvider) filters() []validation.Filter {
  return []validation.Filter{
    &filters.ToInt{},
  }
}
Edit this page
Prev
Session
Next
Logging