本节摘要:表单是项目与用户对话最频繁的界面,也是安全重灾区。本节用"新建工单"这个完整表单,把旧输入回填、验证错误展示、CSRF 令牌三个关键机制一次讲透,并拆开 CSRF 攻击的原理看框架的令牌机制为什么有效;最后把 Livewire 这条服务端驱动的动态化路线放进视野,给出装修工艺的选型建议。
布局和组件就位了,现在给工单系统开一扇真正能用的窗:新建工单表单。它要经得起三个考验:提交失败后用户填的东西不丢;错误信息说得清哪个字段哪里不对;陌生人不能借用户的登录态伪造提交。三道考验对应三个机制:old 回填、errors 展示、CSRF 令牌。
{{-- resources/views/orders/create.blade.php --}} @extends('layouts.app') @section('content') <h1>登记新工单</h1> {{-- 验证失败后的错误包:errors 由框架自动注入所有视图 --}} @if ($errors->any()) <div class="alert"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif {{-- 上一节组件化的提示条也可以直接复用 --}} @if (session('status')) <x-status-badge :status="session('status')" /> @endif <form method="POST" action="{{ route('orders.store') }}"> @csrf {{-- 防伪造令牌:没有这行,提交会被 419 拒绝 --}} <label>标题 {{-- old 回填:验证失败跳回来时,用户填过的还在 --}} <input type="text" name="title" value="{{ old('title') }}"> </label> @error('title') <p class="err">{{ $message }}</p> @enderror <label>预算(元) <input type="number" name="budget" value="{{ old('budget', 10000) }}"> </label> @error('budget') <p class="err">{{ $message }}</p> @enderror <label>指派工人 <select name="workers[]" multiple> @foreach ($workers as $worker) <option value="{{ $worker->id }}" @in_array($worker->id, (array) old('workers', [])) selected >{{ $worker->name }}</option> @endforeach </select> </label> <button type="submit">提交工单</button> </form> @endsection
三个机制各管一件事。old('title') 从闪存里取上一次提交的输入,第二个参数是默认值;@error 指令按字段精确展示该字段的错误消息;@csrf 展开成一个隐藏令牌字段,与 session 里的值配对验签。多选回填用 @in_array 指令判断选中态,配合 old('workers', []) 对首次进入页面的情况兜底。
跨站请求伪造的攻击剧本:你登录着工单系统,又顺手点开一个恶意网页;那个网页里藏着一个自动提交的表单,目标正是工单系统的删除接口。浏览器发请求时会自动带上你的 Cookie,服务器一看"登录态有效",删除就执行了——用户全程不知情。
令牌机制的破解思路:表单里埋一个随 session 生成的随机令牌,提交时服务端核对。恶意网页拿不到这个令牌(浏览器同源策略挡着它读别站页面),它伪造的请求缺令牌,直接 419 拒绝。这就是 web 中间件组里 VerifyCsrfToken 那道工序每天在干的事。三条使用纪律:
⚠️ 常见坑:看到 419 状态码别慌,它几乎总是三件事之一——表单漏了 @csrf、session 过期后用户长时间停留再提交、AJAX 没带令牌头。照单排查即可。
静态装修管不了"选中即刷新、输入即搜索"这类动态交互。Laravel 给出一条光谱,按引入复杂度从小到大:
| 路线 | 原理 | 适合 | 代价 |
|---|---|---|---|
| 纯 Blade 加少量 JS | 服务端渲染为主 | 内容站、管理后台 | 复杂交互写得吃力 |
| Livewire | 交互走 AJAX,服务端重算重渲染 | 中等动态性、全栈团队 | 每次交互都有网络往返 |
| Inertia 加 Vue 或 React | 前后端同仓、客户端渲染 | 重交互产品、有前端成员 | 两套技术栈的心智负担 |
Livewire 的体验值得亲手试一次:组件写 PHP 加 Blade,交互逻辑全在服务端,前端零构建。一个实时搜索框十行代码:
<?php namespace App\Livewire; use App\Models\WorkOrder; use Livewire\Component; class OrderSearch extends Component { public string $keyword = ''; public function render() { return view('livewire.order-search', [ 'orders' => WorkOrder::query() ->where('title', 'like', "%{$this->keyword}%") ->limit(10)->get(), ]); } }
{{-- resources/views/livewire/order-search.blade.php --}} <div> {{-- wire.model 双向绑定:输入变化自动同步服务端属性并触发重渲染 --}} <input type="text" wire:model.live.debounce.300ms="keyword"> <ul> @foreach ($orders as $order) <li>{{ $order->sn }} · {{ $order->title }}</li> @endforeach </ul> </div>
选型建议保持朴素:团队没有专职前端就别急着上 Inertia;交互密度不高时 Livewire 的性价比很高;真到重交互产品级,再引入完整前端栈不迟。第六章到此装修完工,下一章给工地装门禁:认证、授权与 API 通行证。
到此为止,项目谁都能打开、谁都能提交。下一章装门禁:让系统开始认人、辨权、发通行证。