5.7 KiB
5.7 KiB
3.4 FormRequest 验证器
:::tip 概述
参数格式验证由 FormRequest 完成(Laravel 标准机制):控制器方法类型注入请求类,未通过校验时框架自动返回统一 JSON 错误响应。所有请求类继承本项目的 BaseRequest,子类只需声明 rules() 与 messages()。
:::
请求类文件位置
app/Http/Requests/ExampleRequest.php
完整代码(来自项目实际代码)
<?php
namespace App\Http\Requests;
/**
* 案例表单请求
*
* 负责案例演示新增/修改接口的参数校验。
*
* 说明:
* - 继承 BaseRequest,基础校验能力(失败响应统一处理)由基类提供。
* - 本类只声明案例特有的字段规则与自定义错误消息。
* - 新增与修改共用本请求类,修改时由基类或控制器负责校验 id。
* - 前端传参使用 camelCase,数据库字段为 snake_case;
* - type 类型:1=类型1,2=类型2,3=类型3,4=类型4,
* 与 Example 模型的约定一致。
* - status 状态:1=在用,2=停用。
*/
class ExampleRequest extends BaseRequest
{
/**
* 校验规则
*
* @return array<string, string> 字段规则
*/
public function rules(): array
{
return [
'name' => 'required|string|max:255', // 案例名称:必填,最长 255
'avatar' => 'nullable|string|max:255', // 头像/图片地址:可空,最长 255
'type' => 'nullable|integer|in:1,2,3,4', // 类型:1=类型1,2=类型2,3=类型3,4=类型4
'status' => 'nullable|integer|in:1,2', // 状态:1=在用,2=停用
'sort' => 'nullable|integer', // 排序值:可空,整数
];
}
/**
* 自定义错误消息
*
* 为需要友好提示的规则提供中文消息,覆盖框架默认英文提示。
*
* @return array<string, string> 字段规则 => 错误消息
*/
public function messages(): array
{
return [
'name.required' => '案例名称不能为空',
'name.max' => '案例名称不能超过 255 个字符',
'avatar.max' => '图片地址不能超过 255 个字符',
'type.in' => '案例类型不合法,允许:1-类型1 2-类型2 3-类型3 4-类型4',
'status.in' => '状态值不合法,允许:1-在用 2-停用',
'sort.integer' => '排序值格式不正确',
];
}
}
控制器中的使用
// 类型注入:请求到达方法前自动完成校验
public function add(ExampleRequest $request): JsonResponse
{
// validated() → 已校验数据,键名转换由 Service 写入入口统一完成
return $this->_add($this->exampleService, $request->validated());
}
:::tip validated() 是白名单
validated() 只返回 rules() 中声明过的键,未声明的字段在进入控制器前即被丢弃。业务需要传递给 Service 的字段(如修改接口的 id、用户表单的 city / roles)都应在规则中声明,否则即使前端传了也到不了 Service。
:::
BaseRequest 基类提供的能力
app/Http/Requests/BaseRequest.php:
| 能力 | 说明 |
|---|---|
authorize() 统一返回 true |
鉴权由路由中间件与 #[Permission] 注解负责,请求类不参与授权 |
failedValidation() 统一处理 |
校验失败返回固定 JSON:{"code":1,"ok":false,"msg":"参数验证失败","data":{字段=>错误数组}}(code=1 与 Controller::fail() 约定一致) |
:::warning 两条校验失败路径
- FormRequest 注入(本章方式)→ 失败响应
code=1; - 代码内手动
Validator::validate()抛出的ValidationException→ 由全局 Handler 渲染为code=422。
前端两种都应兼容(以 code !== 0 判失败)。详见 5.16 统一响应与异常抛出。
:::
常用验证规则
| 规则 | 说明 | 示例 |
|---|---|---|
required |
必填 | 'name' => 'required' |
string |
字符串 | 'name' => 'string' |
max:n |
最大长度 | 'title' => 'max:200' |
min:n |
最小长度 | 'password' => 'min:6' |
integer |
整数 | 'sort' => 'integer' |
in:a,b,c |
枚举值 | 'status' => 'in:1,2' |
nullable |
可空(不传则跳过其余规则) | 'avatar' => 'nullable|string' |
email |
邮箱格式 | 'email' => 'email' |
date |
日期格式 | 'start_time' => 'date' |
regex:/.../ |
正则 | 'code' => 'regex:/^[A-Za-z]+$/' |
分层职责:格式 vs 业务
| 层 | 职责 | 示例 |
|---|---|---|
| FormRequest(本章) | 参数格式:必填、类型、长度、枚举 | name 必填、type 只能 1-4 |
| Service 层 | 参数业务规则:唯一性、状态依赖、权限 | name 不能与已有记录重复 |
:::tip 唯一性为什么不放 FormRequest?
uniqueFields 校验在 Service 层完成(HasUniqueCheck Trait),因为软删除场景下数据库唯一索引会误伤(is_delete=1 的旧记录仍占索引),且需要排除自身(update 场景)。见 3.5 Service 层开发。
:::
场景拆分(新增 vs 修改字段差异大时)
FormRequest 可按方法区分规则:
class UserRequest extends BaseRequest
{
public function rules(): array
{
// 通过 $this->isMethod('post') 或自定义场景标识区分
$rules = [
'realname' => 'required|string|max:50',
'mobile' => 'nullable|max:20',
];
if ($this->isMethod('post')) {
// 新增时才要求用户名
$rules['username'] = 'required|string|min:3|max:50';
}
return $rules;
}
}