goravel
  • README
  • ORM
    • getting-started
    • Migrations
    • Relationships
  • Architecutre Concepts
    • Facades
    • Request Lifecycle
    • Service Container
    • Service Providers
  • Digging Deeper
    • Artisan Console
    • Cache
    • Events
    • File Storage
    • Helpers
    • Mail
    • Mock
    • Package Development
    • Queues
    • Task Scheduling
  • Getting Started
    • Compile
    • Configuration
    • Directory Structure
    • Installation
  • prologue
    • Contribution Guide
    • Excellent Extend Packages
  • security
    • Authentication
    • Authorization
    • Encryption
    • Hashing
  • The Basics
    • Controllers
    • Grpc
    • Logging
    • Middleware
    • HTTP Requests
    • HTTP Response
    • Routing
    • Validation
  • upgrade
    • History Upgrade
    • Upgrading To v1.1 From v1.0
    • Upgrading To v1.10 From v1.9
    • Upgrading To v1.11 From v1.10
    • Upgrading To v1.12 From v1.11
    • Upgrading To v1.2 From v1.1
    • Upgrading To v1.3 From v1.2
    • Upgrading To v1.4 From v1.3
    • Upgrading To v1.5 From v1.4
    • Upgrading To v1.6 From v1.5
    • Upgrading To v1.7 From v1.6
    • Upgrading To v1.8 From v1.7
    • Upgrading To v1.9 From v1.8
  • zh
    • ORM
      • 快速入门
      • 数据库迁移
      • 模型关联
    • 核心架构
      • Facades
      • 请求周期
      • 服务容器
      • 服务提供者
    • 综合话题
      • Artisan 命令行
      • 缓存系统
      • 事件系统
      • 文件存储
      • 辅助函数
      • 发送邮件
      • Mock
      • 扩展包开发
      • 队列
      • 任务调度
    • 入门指南
      • 编译
      • 配置信息
      • 文件夹结构
      • 安装
    • prologue
      • 贡献指南
      • 优秀扩展包
    • security
      • 用户认证
      • 用户授权
      • 加密解密
      • 哈希
    • 基本功能
      • 控制器
      • Grpc
      • 日志
      • HTTP 中间件
      • 请求
      • 响应
      • 路由
      • 表单验证
    • upgrade
      • 历史版本升级
      • 从 v1.0 升级到 v1.1
      • 从 v1.9 升级到 v1.10
      • 从 v1.10 升级到 v1.11
      • 从 v1.11 升级到 v1.12
      • 从 v1.1 升级到 v1.2
      • 从 v1.2 升级到 v1.3
      • 从 v1.3 升级到 v1.4
      • 从 v1.4 升级到 v1.5
      • 从 v1.5 升级到 v1.6
      • 从 v1.6 升级到 v1.7
      • 从 v1.7 升级到 v1.8
      • 从 v1.8 升级到 v1.9
Powered by GitBook
On this page
  • Introduction
  • Validation Quickstart
  • Defining The Routes
  • Creating The Controller
  • Writing The Validation Logic
  • A Note On Nested Attributes
  • Form Request Validation
  • Creating Form Requests
  • Authorizing Form Requests
  • 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 Messages Exist For A Field
  • Available Validation Rules
  • Custom Validation Rules
  • Points For Using Rules
  • int
Edit on GitHub
  1. The Basics

Validation

[[toc]]

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

To learn about Goravel's powerful validation features, let's look at a complete example of validating a form and displaying the error messages back to the user. By reading this high-level overview, you'll be able to gain a good 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 will display a form for the user to create a new blog post, while the POST route will store the new blog 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",
  })
}

A Note On Nested Attributes

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

validator, err := ctx.Request().Validate(map[string]string{
  "title": "required|max_len:255",
  "author.name": "required",
  "author.description": "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 two methods: Authorize, Rules, Messages, Attributes and PrepareForValidation.

As you might have guessed, 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 key are consistent with the incoming key.
    "name": "required|max_len:255",
  }
}

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:

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

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

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.

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(data validation.Data) {
  if name, exist := data.Get("name"); exist {
    _, _ = data.Set("name", name.(string)+"1")
  }
}

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 on the facade generates a new validator instance:

func (r *PostController) Store(ctx http.Context) {
  validator, err := 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 that can be map[string]any or struct. The second argument is an array of the validation rules that should 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"
)

validator, err := facades.Validation().Make(input, rules, validation.PrepareForValidation(func(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 reuqests or manually created calidator instances, you still want tot bind the request data to struct, there are two ways to do this:

  1. Use the Bind methed, 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 are 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 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:

Name
Description

required

Check value is required and cannot be empty.

required_if

required_if:anotherfield,value,... The field under validation must be present and not empty if the anotherField field is equal to any value.

required_unless

required_unless:anotherfield,value,... The field under validation must be present and not empty unless the anotherField field is equal to any value.

required_with

required_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_all

required_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_without

required_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_all

required_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.

int

uint

Check value is uint(uintX) type, value >= 0

bool

Check value is bool string(true: "1", "on", "yes", "true", false: "0", "off", "no", "false").

string

Check value is string type, and support size checking. eg:string string:2 string:2,12

float

Check value is float(floatX) type

slice

Check value is slice type([]intX []uintX []byte []string)

in

in:foo,bar,… Check if the value is in the given enumeration

not_in

not_in:foo,bar,… Check if the value is not in the given enumeration

starts_with

starts_with:foo Check if the input string value is starts with the given sub-string

ends_with

ends_with:foo Check if the input string value is ends with the given sub-string

between

between:min,max Check that the value is a number and is within the given range

max

max:value Check value is less than or equal to the given value(intX uintX floatX)

min

min:value Check value is greater than or equal to the given value(intX uintX floatX)

eq

eq:value Check that the input value is equal to the given value

ne

ne:value Check that the input value is not equal to the given value

lt

lt:value Check value is less than the given value(intX uintX floatX)

gt

gt:value Check value is greater than the given value(intX uintX floatX)

len

len:value Check value length is equals to the given size(string array slice map)

min_len

min_len:value Check the minimum length of the value is the given size(string array slice map)

max_len

max_len:value Check the maximum length of the value is the given size(string array slice map)

email

Check value is email address string

array

Check value is array, slice type

map

Check value is a MAP type

eq_field

eq_field:field Check that the field value is equals to the value of another field

ne_field

ne_field:field Check that the field value is not equals to the value of another field

gt_field

gte_field:field Check that the field value is greater than the value of another field

gte_field

gt_field:field Check that the field value is greater than or equal to the value of another field

lt_field

lt_field:field Check that the field value is less than the value of another field

lte_field

lte_field:field Check if the field value is less than or equal to the value of another field

file

Verify if it is an uploaded file

image

Check if it is an uploaded image file and support suffix check

date

Check the field value is date string

gt_date

gt_date:value Check that the input value is greater than the given date string

lt_date

lt_date:value Check that the input value is less than the given date string

gte_date

gte_date:value Check that the input value is greater than or equal to the given date string

lte_date

lte_date:value Check that the input value is less than or equal to the given date string

alpha

Verify that the value contains only alphabetic characters

alpha_num

Check that only letters, numbers are included

alpha_dash

Check to include only letters, numbers, dashes ( - ), and underscores ( _ )

json

Check value is JSON string

number

Check value is number string >= 0

full_url

Check value is full URL string(must start with http,https)

ip

Check value is IP(v4 or v6) string

ipv4

Check value is IPv4 string

ipv6

Check value is IPv6 string

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 may use the make:rule Artisan command. Let's use this command to generate a rule that verifies a string is uppercase. Goravel will place the new rule in the app/rules directory. If this directory does not exist, Goravel will create it when you execute the Artisan command to create your rule:

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

Once the rule has been created, we are ready to define its behavior. A rule object contains two method: Passes and Message. This Passes method receives the all data, data to be validated and validate parameters, it should return true or false based on whether the attribute value is valid. The Message method should return the validation error message that should be used when 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{},
  }
}

Points For Using Rules

int

当时用 ctx.Request().Validate(rules) 进行校验时,传入的 int 类型数据将会被 json.Unmarshal 解析为 float64 类型,从而导致 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 2: Use facades.Validation().Make() for rule validation;

PreviousRoutingNextupgrade

Last updated 1 year ago

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 within this method:

Check value is intX uintX type, and support size checking. eg: int int:2 int:2,12. Notice:

Option 1: Add , format the data before validating the data;

authorization gates and policies
validation.PrepareForValidation
Points for using rules