路由参数必须用 where() 限定类型才安全,否则 /users/abc 等非法路径可绕过校验;整数用 [0-9]+、UUID 用正则、中文需确认服务器编码并配合 mb_strlen;API 与 Web 路由验证策略须分离,API 应返回 422 JSON 响应。

路由参数必须用 where() 限定类型才安全
直接在路由定义里写 {id} 不代表它自动是数字,Laravel 默认全当字符串处理。不加约束的话,/users/abc、/users/1%20 都能进控制器,后面查数据库可能报错或绕过权限检查。
实操建议:
- 整数 ID 必须加
where('id', '[0-9]+'),别信->middleware('throttle:api')能挡住非法参数 - UUID 类型用
where('id', '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}'),别只靠模型里的->uuid()校验 - 中文路径参数?先确认 Nginx/Apache 是否转发了 UTF-8 编码,再用
where('name', '.*')+ 控制器里mb_strlen($name) 手动截断
Laravel 9+ 的 Route::bind() 不等于验证,只是绑定逻辑
Route::bind('user', function ($value) { return User::findOrFail($value); }); 这段代码只负责找模型,不校验 $value 是不是合法格式。如果 $value 是 SQL 注入片段(比如 1 OR 1=1),findOrFail() 会直接抛异常,但错误堆栈暴露了底层结构。
实操建议:
- 绑定前先过滤:把
Route::bind()换成闭包里先做filter_var($value, FILTER_VALIDATE_INT)或正则匹配 - 避免在
bind()里调用复杂查询——它会在每次匹配路由时执行,高并发下容易拖慢整个路由解析 - 如果要用软删除模型,
findOrFail()会失败,得改成User::where('id', $value)->withTrashed()->firstOrFail(),但记得后续手动检查trashed()
用 Request 类做参数验证,别在控制器里手写 if (!is_numeric())
有人在控制器里写 if (!is_numeric($id) || $id ,这既重复又漏场景(比如浮点数 <code>1.5 过了 is_numeric() 却插不进整型主键)。Laravel 的表单请求验证才是为这种场景设计的。
实操建议:
- 生成请求类:
php artisan make:request GetUserRequest,在rules()里写['id' => ['required', 'integer', 'min:1']] - 注意
integer规则默认允许字符串数字(如"123"),要严格类型用'id' => ['required', 'int', 'min:1'](int是 Laravel 9.2+ 新增的严格整型规则) - 批量验证多个参数时,别在
rules()里硬编码字段名,用request()->route()->parameters()动态取,避免漏掉新增路由参数
API 路由和 Web 路由的验证策略不能混用
Web 路由出错跳 404 页面,API 路由该返回 422 Unprocessable Entity。但很多人共用同一个 GetUserRequest,结果 API 请求带非法 ID 时返回 HTML 错误页,前端解析直接崩溃。
实操建议:
- API 请求类继承
FormRequest后,重写failedValidation(),用response()->json(..., 422)替换默认行为 - Web 路由用
validate()方法,API 路由统一走validated()+ 自定义异常处理器,别依赖全局App\Exceptions\Handler一刀切 - 别在
web.php里注册 API 路由——中间件栈不同,VerifyCsrfToken对 API 是冗余甚至有害的
最常被忽略的是:路由参数验证和模型查询是两层事。哪怕 where() 锁死了格式,find() 仍可能返回 null;哪怕 Request 类校验通过,数据库唯一索引冲突还是会发生。验证只是第一道门,不是保险柜。



















