4.2 表单验证:材料进场质检单


4.2 表单验证:材料进场质检单

本节摘要:验证是写库前的最后一道闸:规则声明、错误消息、授权检查、数据清洗都集中在 Form Request 这张质检单上。本节从内联验证讲到 Form Request,覆盖常用规则、自定义规则、条件验证与数组输入,最后给一套"验证失败的响应长什么样"的完整说明。读完你能在任何模块里五分钟开出一合格的质检单。

为什么不能信用户提交的数据

用户会手滑、会复制错、会有意试探。没有验证的接口,等于材料不经验收直接上楼:邮箱字段里塞一段脚本(第六章讲 XSS 时你会再想起这句话)、金额传成负数、状态字段被改成别人的值。验证的目的有两层:拦住明显不合格的材料,同时把"哪些字段、什么规格"白纸黑字写下来——半年后没人记得接口吃什么,质检单就是接口文档。

先看内联写法,再升级

控制器里随手可用的验证是这样:

<?php public function store(Request $request) { $data = $request->validate([ 'title' => ['required', 'string', 'max:100'], 'budget' => ['required', 'integer', 'min:0', 'max:1000000'], 'due_at' => ['required', 'date', 'after:today'], 'team_id' => ['required', 'exists:teams,id'], ]); // $data 只含通过验证的字段,天然滤掉了多余输入 $order = WorkOrder::create($data); }

validate 失败时,普通表单请求自动带着错误信息重定向回上一页;期望 JSON 的请求(带 Accept 头)自动收到 422 响应。小模块这么写完全够。问题出在规模:规则一多,动作膨胀;两个接口共用一套规则,就得复制粘贴。升级方案是把整张质检单抽成一个类。

Form Request:一张完整的质检单

php artisan make:request StoreOrderRequest
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class StoreOrderRequest extends FormRequest { // 授权:这个人能不能提这张单(第七章会升级成 Policy) public function authorize(): bool { return $this->user()->can('create', WorkOrder::class); } // 规则:一条一行,可读性优先 public function rules(): array { return [ 'title' => ['required', 'string', 'max:100'], 'budget' => ['required', 'integer', 'min:0'], 'due_at' => ['required', 'date', 'after:today'], 'team_id' => ['required', 'exists:teams,id'], 'workers' => ['required', 'array', 'min:1'], 'workers.*' => ['integer', 'exists:workers,id'], 'remark' => ['nullable', 'string', 'max:500'], ]; } // 错误消息:说人话 public function messages(): array { return [ 'title.required' => '工单标题不能为空', 'budget.min' => '预算不能是负数', 'workers.*.exists' => '选到的施工人员不存在', ]; } // 验证前的数据清洗:顺手把输入规整了 protected function prepareForValidation(): void { $this->merge([ 'title' => trim((string) $this->input('title')), ]); } }

控制器里的用法干净到只剩类型声明:

<?php public function store(StoreOrderRequest $request): RedirectResponse { // 走到这说明授权与验证都过了;validated() 只给通过规则的字段 $order = WorkOrder::create($request->validated()); return redirect()->route('orders.show', $order); }

⚠️ 常见坑:永远用 validated() 而不是 all() 去取数据。all() 会把没声明规则的字段也带进来,攻击者多提交一个 status 字段就可能覆盖工作流状态——批量赋值漏洞的经典入口。fillable 白名单(第五章讲)是第二道防线,但第一道永远是 validated()。

图 4-2:一张质检单的完整流程

图 4-2:一张质检单的完整流程

进阶三招:自定义规则、条件验证、数组输入

特殊规格(比如"预算必须是百的倍数")写成自定义规则对象,规则库就能沉淀复用:

<?php // php artisan make:rule MultipleOfHundred namespace App\Rules; use Closure; use Illuminate\Contracts\Validation\ValidationRule; class MultipleOfHundred implements ValidationRule { public function validate(string $attribute, mixed $value, Closure $fail): void { if (((int) $value) % 100 !== 0) { $fail(':attribute 必须是整百金额'); } } } // 使用:'budget' => ['required', 'integer', new MultipleOfHundred],

字段之间有依赖时用条件规则;数组输入用点号逐层下钻,上面质检单里的 workers.* 就是逐个验证数组元素的写法,workers.*.id 则能钻到嵌套对象的字段。表单请求与数据库唯一性结合的 unique:orders,sn 规则,等到第五章建好表之后会真正跑起来。

本节要点回顾

  • 验证双层目的:拦不合格材料 + 充当接口规格文档。
  • Form Request 四道工序:授权、清洗、规则、放行,控制器只剩类型声明加 validated()。
  • 取数铁律:validated() 替代 all(),把批量赋值攻击挡在门外。
  • 失败自动响应:表单回跳带旧输入,JSON 回 422 错误对象,都不用手工拼。

材料验完了,该交货了。下一节讲响应构建与 API 资源:页面、跳转、下载、JSON 这些交付物怎么发得标准。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U