请求表单验证及错误处理大全

简介
Laravel 提供了多种方法来验证请求输入数据。默认情况下,Laravel 的控制器基类使用 ValidatesRequests
trait,该 trait 提供了便捷方法通过各种功能强大的验证规则来验证输入的 HTTP 请求。
快速入门
要掌握 Laravel 强大的验证特性,让我们先看一个完整的验证表单并返回错误信息给用户的示例。
定义路由
首先,我们假定在 routes/web.php
文件中包含如下路由:
显然,GET
路由为用户显示了一个创建新的博客文章的表单,POST 路由将新的博客文章存储到数据库。
创建控制器
接下来,让我们看一个处理这些路由的简单控制器示例。我们先将 store
方法留空:
编写验证逻辑
现在我们准备用验证新博客文章输入的逻辑填充 store
方法。我们使用 Illuminate\Http\Request
对象提供的 validate
方法来实现这一功能,如果验证规则通过,代码将会继续往下执行;反之,如果验证失败,将会抛出一个异常,相应的错误响应也会自动发送给用户。在这个传统的 HTTP 请求案例中,将会生成一个重定向响应,如果是 AJAX 请求则会返回一个 JSON 响应。
要更好地理解 validate
方法,让我们回顾下 store
方法:
正如你所看到的,我们只是传入期望的验证规则到 validate
方法。再强调一次,如果验证失败,相应的响应会自动生成。如果验证通过,控制器将会继续往下执行。
注:实际执行代码之前,需要在数据库中创建posts
数据表,因为这里用到了unique:posts
这个验证规则,该规则会去数据库中查询传入标题是否已存在以保证唯一性。
首次验证失败后中止后续规则验证
有时候你可能想要在首次验证失败后停止检查该属性的其它验证规则,要实现这个功能,可以在规则属性中分配 bail
作为首规则:
在这个例子中,如果 title
属性上的 required
规则验证失败,则不会检查 unique
规则,规则会按照分配顺序依次进行验证。
嵌套属性注意事项
如果 HTTP 请求中包含“嵌套”参数,可以使用“.”在验证规则中指定它们:
这样的验证规则适用于验证如下标签请求:
显示验证错误信息
那么,如果请求输入参数没有通过给定验证规则怎么办?正如前面所提到的,Laravel 将会自动将用户重定向回上一个位置。此外,所有验证错误信息会自动存放到一次性 Session,我们可以看下这个 Session 数据的数据结构:
注意我们并没有在 GET
路由中显式绑定错误信息到视图。这是因为 Laravel 总是从 Session 数据中检查错误信息,而且如果有的话会自动将其绑定到视图。所以,值得注意的是每次请求的所有视图中总是存在一个$errors
变量,从而允许你在视图中方便而又安全地使用。$errors
变量是一个Illuminate\Support\MessageBag
实例。想要了解更多关于该对象的信息,查看其对应文档。
注:$errors
变量会通过web
中间件组中的Illuminate\View\Middleware\ShareErrorsFromSession
中间件绑定到视图,如果使用了该中间件,那么$errors
变量在视图中总是有效,从而方便你随时使用。
所以,在我们的例子中,验证失败的话用户将会被重定向到控制器的 create
方法,从而允许我们在视图中显示错误信息:
如果验证失败的话,跳转回表单页面的同时会显示错误信息:

可选字段注意事项
默认情况下,Laravel自带了 TrimStrings
和 ConvertEmptyStringsToNull
中间件,这两个中间件位于 App\Http\Kernel
类的全局中间件堆栈中,因为这个原因,你需要经常将“可选”的请求字段标记为 nullable
—— 如果你不想让验证器将 null
判定为无效的话。例如:
在这个例子中,我们指定 publish_at
字段可以为 null
或者有效的日期格式。如果 nullable
没有被添加到验证规则,验证器会将 null
判定为无效日期。
AJAX 请求&验证
在上面的例子中,我们使用了传统的表单来发送数据到应用。不过,现实场景中,很多应用使用 AJAX 请求。在 AJAX 请求中使用 validate
方法时,Laravel 不会生成重定向响应。取而代之的,Laravel 生成一个包含验证错误信息的 JSON 响应。该 JSON 响应会带上一个 HTTP 状态码 422
。
复杂表单请求验证
创建表单请求
对于更复杂的验证场景,你可能想要创建一个“表单请求”。表单请求是包含验证逻辑的自定义请求类,要创建表单验证类,可以使用 Artisan 命令 make:request
:
生成的类位于 app/Http/Requests
目录下,如果该目录不存在,运行 make:request
命令时会替我们生成。接下来我们添加少许验证规则到该类的 rules
方法:
那么,验证规则如何生效呢?你所要做的就是在控制器方法中类型提示该请求类。这样表单输入请求会在控制器方法被调用之前被验证,这就是说你不需要将控制器方法和验证逻辑杂糅在一起:
如果验证失败,重定向响应会被生成并将用户退回上一个位置,错误信息也会被存储到一次性 Session 以便在视图中显示。如果是 AJAX 请求,带 422
状态码的 HTTP 响应将会返回给用户,该响应数据中还包含了 JSON 格式的验证错误信息。
添加验证后钩子到表单请求
如果你想要添加“验证后”钩子到表单请求,可以使用 withValidator
方法。该方法接收完整的构造验证器,从而允许你在验证规则执行前调用任何验证器方法:
授权表单请求
由于所有请求都继承自 Laravel 请求基类,我们可以使用 user
方法获取当前认证用户,还要注意上面这个例子中对 route
方法的调用。该方法赋予用户访问被调用路由 URI 参数的权限,比如下面这个例子中的 {comment}
参数:
如果你计划在应用的其他部分调用授权逻辑,只需在 authorize
方法中简单返回 true
即可:
自定义错误消息
你可以通过重写 messages
方法自定义表单请求使用的错误消息,该方法应该返回属性/规则对数组及其对应错误消息:
手动创建验证器
如果你不想使用请求实例上的 validate
方法,可以使用 Validator
门面手动创建一个验证器实例,该门面提供的 make
方法可用于生成一个新的验证器实例:
传递给 make
方法的第一个参数是需要验证的数据,第二个参数是要应用到数据上的验证规则。
检查请求没有通过验证后,可以使用 withErrors
方法将错误数据存放到一次性 Session,使用该方法时,$errors
变量重定向后自动在视图间共享,从而允许你轻松将其显示给用户,withErrors
方法接收一个验证器、或者一个 MessageBag
,又或者一个 PHP 数组。
自动重定向
如果你想要手动创建一个验证器实例,但仍然使用请求实例的 validate
方法提供的自动重定向,可以调用已存在验证器实例上的 validate
方法,如果验证失败,用户将会被自动重定向,或者,如果是 AJAX 请求的话,返回 JSON 响应:
命名错误包
如果你在单个页面上有多个表单,可能需要命名错误的 MessageBag
,从而允许你为指定表单获取错误信息。只需要传递名称作为第二个参数给 withErrors
即可:
然后你就可以从 $errors
变量中访问命名的 MessageBag
实例:
验证钩子之后
验证器允许你在验证完成后添加回调,这种机制允许你轻松执行更多验证,甚至添加更多错误信息到消息集合。使用验证器实例上的 after
方法即可:
处理错误信息
调用 Validator
实例上的 errors
方法之后,将会获取一个 Illuminate\Support\MessageBag
实例,该实例中包含了多种处理错误信息的便利方法。在所有视图中默认有效的 $errors
变量也是一个 MessageBag
实例。
获取某字段的第一条错误信息
要获取指定字段的第一条错误信息,可以使用 first
方法:
获取指定字段的所有错误信息
如果你想要简单获取指定字段的所有错误信息数组,使用 get
方法:
如果是一个数组表单字段,可以使用 *
获取所有数组元素错误信息:
获取所有字段的所有错误信息
要获取所有字段的所有错误信息,可以使用 all
方法:
判断消息中是否存在某字段的错误信息
has
方法可用于判断错误信息中是否包含给定字段:
自定义错误信息
如果需要的话,你可以使用自定义错误信息替代默认的,有多种方法来指定自定义信息。首先,你可以传递自定义信息作为第三个参数给 Validator::make
方法:
在本例中,:attribute
占位符将会被验证时实际的字段名替换,你还可以在验证消息中使用其他占位符,例如:
为给定属性指定自定义信息
有时候你可能只想为特定字段指定自定义错误信息,可以通过“.”来实现,首先指定属性名,然后是规则:
在语言文件中指定自定义消息
在很多案例中,你可能想要在语言文件中指定自定义消息而不是将它们直接传递给 Validator
。要实现这个,添加消息到 resources/lang/xx/validation.php
语言文件的 custom
数组:
在语言文件中指定自定义属性
如果你想要将验证消息的 :attribute
部分替换成自定义属性名称,可以在语言文件 resources/lang/xx/validation.php
的 attributes
数组中指定自定义名称:
验证规则大全
下面是有效规则及其函数列表:
Accepted
Active URL
After (Date)
After Or Equal(Date)
Alpha
Alpha Dash
Alpha Numeric
Array
Before (Date)
Before Or Equal(Date)
Between
Boolean
Confirmed
Date
Date Equals
Date Format
Different
Digits
Digits Between
Dimensions(图片文件)
Distinct
E-Mail
Exists (Database)
File
Filled
Image (File)
In
In Array
Integer
IP Address
JSON
Max
MIME Types (File)
MIME Type By File Extension
Min
Nullable
Not In
Numeric
Present
Regular Expression
Required
Required If
Required Unless
Required With
Required With All
Required Without
Required Without All
Same
Size
String
Timezone
Unique (Database)
URL
accepted
验证字段的值必须是 yes
、on
、1
或 true
,这在“同意服务协议”时很有用。
active_url
验证字段必须是基于 PHP 函数 dns_get_record
的,有 A 或 AAAA 记录的值。
after:date
验证字段必须是给定日期之后的一个值,日期将会通过 PHP 函数 strtotime
传递:
你可以指定另外一个与日期进行比较的字段,而不是传递一个日期字符串给 strtotime
执行:
after_or_equal:date
验证字段必须是大于等于给定日期的值,更多信息,请参考 after:date
规则。
alpha
验证字段必须是字母。
alpha_dash
验证字段可以包含字母和数字,以及破折号和下划线。
alpha_num
验证字段必须是字母或数字。
array
验证字段必须是 PHP 数组。
before:date
和 after:date
相对,验证字段必须是指定日期之前的一个数值,日期将会传递给 PHP strtotime
函数。
before_or_equal:date
验证字段必须小于等于给定日期。日期将会传递给 PHP 的 strtotime
函数。
between:min,max
验证字段大小在给定的最小值和最大值之间,字符串、数字、数组和文件都可以像使用 size
规则一样使用该规则:
boolean
验证字段必须可以被转化为布尔值,接收 true
, false
, 1
, 0
, "1"
和 "0"
等输入。
confirmed
验证字段必须有一个匹配字段 foo_confirmation
,例如,如果验证字段是 password
,必须输入一个与之匹配的 password_confirmation
字段。
date
验证字段必须是一个基于 PHP strtotime
函数的有效日期
date_equals:date
验证字段必须等于给定日期,日期会被传递到 PHP strtotime
函数。
date_format:format
验证字段必须匹配指定格式,可以使用 PHP 函数date 或 date_format 验证该字段。
different:field
验证字段必须是一个和指定字段不同的值。
digits:value
验证字段必须是数字且长度为 value 指定的值。
digits_between:min,max
验证字段数值长度必须介于最小值和最大值之间。
dimensions
验证的图片尺寸必须满足该规定参数指定的约束条件:
有效的约束条件包括:min_width
, max_width
, min_height
, max_height
, width
, height
, ratio
。
ratio 约束宽度/高度的比率,这可以通过表达式 3/2
或浮点数 1.5
来表示:
由于该规则要求多个参数,可以使用 Rule::dimensions
方法来构造该规则:
distinct
处理数组时,验证字段不能包含重复值:
验证字段必须是格式正确的电子邮件地址
exists:table,column
验证字段必须存在于指定数据表
基本使用:
指定自定义列名:
有时,你可能需要为 exists
查询指定要使用的数据库连接,这可以在表名前通过.前置数据库连接来实现:
如果你想要自定义验证规则执行的查询,可以使用 Rule
类来定义规则。在这个例子中,我们还以数组形式指定了验证规则,而不是使用 |
字符来限定它们:
file
验证字段必须是上传成功的文件。
filled
验证字段如果存在则不能为空。
image
验证文件必须是图片(jpeg、png、bmp、gif 或者 svg)
in:foo,bar…
验证字段值必须在给定的列表中,由于该规则经常需要我们对数组进行 implode
,我们可以使用 Rule::in
来构造这个规则:
in_array:另一个字段
验证字段必须在另一个字段值中存在。
integer
验证字段必须是整型。
ip
验证字段必须是IP地址。
ipv4
验证字段必须是IPv4地址。
ipv6
验证字段必须是IPv6地址。
json
验证字段必须是有效的JSON字符串
max:value
验证字段必须小于等于最大值,和字符串、数值、数组、文件字段的 size
规则使用方式一样。
mimetypes:text/plain…
验证文件必须匹配给定的 MIME 文件类型之一:
为了判断上传文件的 MIME 类型,框架将会读取文件内容来猜测 MIME 类型,这可能会和客户端 MIME 类型不同。
mimes:foo,bar,…
验证文件的 MIME 类型必须是该规则列出的扩展类型中的一个
MIME 规则的基本使用:
尽管你只是指定了扩展名,该规则实际上验证的是通过读取文件内容获取到的文件 MIME 类型。
完整的 MIME 类型列表及其相应的扩展可以在这里找到:http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
min:value
与 max:value
相对,验证字段必须大于等于最小值,对字符串、数值、数组、文件字段而言,和 size
规则使用方式一致。
nullable
验证字段可以是 null
,这在验证一些可以为 null
的原始数据如整型或字符串时很有用。
not_in:foo,bar,…
验证字段值不能在给定列表中,和 in
规则类似,我们可以使用 Rule::notIn
方法来构建规则:
numeric
验证字段必须是数值
present
验证字段必须出现在输入数据中但可以为空。
regex:pattern
验证字段必须匹配给定正则表达式。
注:使用 regex
模式时,规则必须放在数组中,而不能使用管道分隔符,尤其是正则表达式中已经使用了管道符号时。
required
验证字段值不能为空,以下情况字段值都为空:
值为
null
值是空字符串
值是空数组或者空的
Coutable
对象值是上传文件但路径为空
required_if:anotherfield,value,…
验证字段在 anotherfield
等于指定值 value
时必须存在且不能为空
required_unless:anotherfield,value,…
除非 anotherfield
字段等于 value
,否则验证字段不能空
required_with:foo,bar,…
验证字段只有在任一其它指定字段存在的情况才是必须的
required_with_all:foo,bar,…
验证字段只有在所有指定字段存在的情况下才是必须的
required_without:foo,bar,…
验证字段只有当任一指定字段不存在的情况下才是必须的
required_without_all:foo,bar,…
验证字段只有当所有指定字段不存在的情况下才是必须的
same:field
给定字段和验证字段必须匹配
size:value
验证字段必须有和给定值 value
相匹配的尺寸/大小,对字符串而言,value
是相应的字符数目;对数值而言,value
是给定整型值;对数组而言,value
是数组长度;对文件而言,value
是相应的文件千字节数(KB)
string
验证字段必须是字符串,如果允许字段为空,需要分配 nullable
规则到该字段。
timezone
验证字符必须是基于 PHP 函数 timezone_identifiers_list
的有效时区标识
unique:table,column,except,idColumn
验证字段在给定数据表上必须是唯一的,如果不指定 column
选项,字段名将作为默认 column
。
指定自定义列名:
自定义数据库连接
有时候,你可能需要自定义验证器生成的数据库连接,正如上面所看到的,设置 unique:users
作为验证规则将会使用默认数据库连接来查询数据库。要覆盖默认连接,在数据表名后使用“.”指定连接:
强制一个忽略给定 ID 的唯一规则:
有时候,你可能希望在唯一检查时忽略给定 ID,例如,考虑一个包含用户名、邮箱地址和位置的”更新属性“界面,你将要验证邮箱地址是唯一的,然而,如果用户只改变用户名字段而并没有改变邮箱字段,你不想要因为用户已经拥有该邮箱地址而抛出验证错误,你只想要在用户提供的邮箱已经被别人使用的情况下才抛出验证错误。
要告诉验证器忽略用户 ID,可以使用 Rule
类来定义这个规则,我们还要以数组方式指定验证规则,而不是使用 |
来界定规则:
如果你的数据表使用主键字段不是 id
,可以在调用 ignore
方法的时候指定字段名称:
添加额外的 where 子句:
使用 where
方法自定义查询的时候还可以指定额外查询约束,例如,下面我们来添加一个验证 account_id
为 1
的约束:
url
验证字段必须是有效的 URL。
添加条件规则
存在时验证
在某些场景下,你可能想要只有某个字段存在的情况下进行验证检查,要快速实现这个,添加 sometimes
规则到规则列表:
在上例中,email
字段只有存在于 $data
数组时才会被验证。
注:如果你尝试验证一个总是存在但可能为空的字段时,参考可选字段注意事项。
复杂条件验证
有时候你可能想要基于更复杂的条件逻辑添加验证规则。例如,你可能想要只有在另一个字段值大于 100 时才要求一个给定字段是必须的,或者,你可能需要只有当另一个字段存在时两个字段才都有给定值。添加这个验证规则并不是一件头疼的事。首先,创建一个永远不会改变的静态规则到 Validator
实例:
让我们假定我们的 Web 应用服务于游戏收藏者。如果一个游戏收藏者注册了我们的应用并拥有超过 100 个游戏,我们想要他们解释为什么他们会有这么多游戏,例如,也许他们在运营一个游戏二手店,又或者他们只是喜欢收藏。要添加这种条件,我们可以使用 Validator
实例上的 sometimes
方法:
传递给 sometimes
方法的第一个参数是我们需要有条件验证的名称字段,第二个参数是我们想要添加的规则,如果作为第三个参数的闭包返回 true
,规则被添加。该方法让构建复杂条件验证变得简单,你甚至可以一次为多个字段添加条件验证:
注:传递给闭包的$input
参数是Illuminate\Support\Fluent
的一个实例,可用于访问输入和文件。
验证数组输入
验证表单数组输入字段不再是件痛苦的事情,例如,如果进入的 HTTP 请求包含 photos[profile]
字段,可以这么验证:
我们还可以验证数组的每个元素,例如,要验证给定数组输入中每个 email
是否是唯一的,可以这么做(这种针对提交的数组字段是二维数组,如 person[][email]
或 person[test][email]
):
类似地,在语言文件中你也可以使用 *
字符指定验证消息,从而可以使用单个验证消息定义基于数组字段的验证规则:
自定义验证规则
使用 Rule 对象
如上所述,Laravel 提供了多种有用的验证规则;不过,你可能还是需要指定一些自己的验证规则。注册自定义验证规则的一种方法是使用规则对象,要生成一个新的规则对象,可以使用 Artisan 命令 make:rule
。下面我们使用这个命令来生成一个用于验证字符串是否是大写的规则,生成的新规则对象类位于 app/Rules
目录:
规则创建之后,就可以定义行为方法,一个规则对象包含两个方法:passes
和 message
,passes
方法接收属性值和名称,并且基于属性值是否有效返回 true
或 false
。message
方法用于在验证失败时返回验证错误消息:
当然,你可以在 message
方法中调用辅助函数 trans
来返回一个在语言文件中定义的错误消息:
规则定义好之后,就可以将其以规则对象实例的方式和其他验证规则一起提供给验证器:
使用扩展
另一个注册自定义验证规则的方式是使用 Validator
门面上的 extend
方法。我们在某个服务提供者(AppServiceProvider
)中使用该方法注册一个自定义验证规则:
自定义验证器闭包接收四个参数:要验证的属性名称、属性值、传递给规则的参数数组以及 Validator
实例。
你还可以传递类和方法到 extend
方法而不是闭包:
定义错误信息
你还需要为自定义规则定义错误信息。你可以使用内联自定义消息数组或者在验证语言文件中添加条目来实现这一功能。消息应该被放到数组的第一维,而不是在只用于存放属性指定错误信息的 custom
数组内:
当创建一个自定义验证规则时,你可能有时候需要为错误信息定义自定义占位符,可以通过创建自定义验证器然后调用 Validator
门面上的 replacer
方法来实现。在服务提供者的 boot
方法中编写如下代码:
隐式扩展
默认情况下,被验证的属性如果没有提供或者验证规则为 required
而值为空,那么正常的验证规则,包括自定义扩展将不会执行。例如,unique
规则将不会检验 null
值:
如果要求即使为空时也要验证属性,则必须要暗示属性是必须的,要创建一个隐式扩展,可以使用 Validator::extendImplicit()
方法:
注:一个隐式扩展仅仅暗示属性是必须的,至于它到底是缺失的还是空值这取决于你。
13 条评论
不需要了
谢谢大神
请问你找到解决办法吗?