Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_NaiveVue/zh/guide/validator.md
T
2026-10-08 17:50:29 +08:00

154 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 3.4 FormRequest 验证器
:::tip 概述
参数格式验证由 **FormRequest** 完成(Laravel 标准机制):控制器方法类型注入请求类,未通过校验时框架自动返回统一 JSON 错误响应。所有请求类继承本项目的 `BaseRequest`,子类只需声明 `rules()` 与 `messages()`。
:::
## 请求类文件位置
```text
app/Http/Requests/ExampleRequest.php
```
## 完整代码(来自项目实际代码)
```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' => '排序值格式不正确',
];
}
}
```
## 控制器中的使用
```php
// 类型注入:请求到达方法前自动完成校验
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 统一响应与异常抛出](/zh/core/exception.html)。
:::
## 常用验证规则
| 规则 | 说明 | 示例 |
|------|------|------|
| `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 层开发](/zh/guide/logic.html)。
:::
## 场景拆分(新增 vs 修改字段差异大时)
FormRequest 可按方法区分规则:
```php
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;
}
}
```