Laravel 10 RESTful API验证异常:唯一字段触发数据库报错
我在使用Laravel 10开发RESTful API时遇到验证异常:提交数据库已存在的唯一字段值时,未触发验证流程返回自定义错误响应,反而出现数据库500内部服务器错误。
相关代码
routes/api.php
Route::controller(AuthController::class)->group(function () { Route::post('register', 'register'); });
app/Controllers/AuthController.php
public function register(RegisterRequest $request) { /** * some code to register the user */ $user = new User; $user->mobile_no = request('mobile'); // 推测此处存在 $user->save() 或类似保存操作 }
app/Requests/RegisterRequest.php
public function rules(): array { return [ 'mobile'=>'required|unique:users,mobile_no|ir_mobile:zero', ]; } public function messages() { return [ 'mobile.unique' => 'a custom msg here', ]; } public function failedValidation(Validator $validator) { // the following function exists in `app/helpers.php` and works currectly throwBadRequestError([ 'errors' => $validator->errors(), 'request' => $_REQUEST ]); }
报错现象
提交重复手机号时,返回500内部服务器错误(数据库唯一约束冲突),而非验证规则定义的400错误响应。
问题原因及解决方法
1. 自定义验证规则ir_mobile异常中断验证流程
如果ir_mobile自定义验证规则在验证失败时直接抛出普通异常(而非返回false),会导致Laravel的验证流程提前终止,后续的unique规则不会执行,控制器方法会继续执行到保存用户操作,最终触发数据库唯一约束错误。
解决:修改ir_mobile规则的实现,确保验证失败时返回false,而非抛出异常。示例:
// 自定义规则示例实现 public function validate($attribute, $value, $parameters) { // 手机号格式校验逻辑 if (!preg_match('/^09\d{9}$/', $value)) { return false; // 返回false触发验证错误 } return true; }
2. throwBadRequestError未抛出正确的异常类型
Laravel表单验证失败时,需要抛出Illuminate\Http\Exceptions\HttpResponseException才能将错误作为HTTP响应返回。如果throwBadRequestError抛出的是普通异常,会被Laravel识别为服务器内部错误(500)。
解决:修改helpers.php中的throwBadRequestError方法:
function throwBadRequestError($data) { throw new \Illuminate\Http\Exceptions\HttpResponseException( response()->json($data, 400) ); }
3. 验证值与存储值格式不一致
如果ir_mobile规则会格式化手机号(比如补充前缀、去除空格等),但验证时使用原始请求值,存储时使用格式化后的值,会导致unique规则无法检测到重复(原始值不重复,但格式化后的值已存在),最终触发数据库约束错误。
解决:在RegisterRequest中添加prepareForValidation方法,提前格式化请求参数,确保验证和存储使用相同的值:
protected function prepareForValidation() { // 示例格式化手机号,根据实际规则调整 $formattedMobile = ltrim($this->mobile, '+'); $this->merge([ 'mobile' => $formattedMobile, ]); }
4. 控制器未使用验证后的参数
建议使用$request->validated()获取经过验证的参数,确保使用的是验证流程处理后的值:
$user->mobile_no = $request->validated()['mobile'];
内容的提问来源于stack exchange,提问作者Arian Sakhaei

