如何禁用Laravel默认ValidationException处理器?
解决Laravel FormRequest验证异常无法被控制器try-catch捕获的问题
问题根源
Laravel的自定义FormRequest会在控制器方法执行之前自动完成请求验证,验证失败时直接抛出ValidationException,并被框架默认的异常处理器捕获返回响应,导致控制器方法内的try-catch块根本不会执行到。
解决方案
方法1:在FormRequest中重写failedValidation方法(推荐)
直接在你的DocumentStoreRequest类中添加该方法,自定义验证失败时的响应格式,这是最贴合Laravel设计模式的方案:
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Contracts\Validation\Validator; use Illuminate\Http\Exceptions\HttpResponseException; class DocumentStoreRequest extends FormRequest { // 原有的rules方法保持不变 public function rules(): array { return [ "citizen_id"=>["required", "exists:citizens,id"], "document_file"=>["required", "image:png"], "type_id"=>["required", new OnlyOneTypeOfDocument($this->citizen_id)] ]; } // 重写验证失败的处理逻辑 protected function failedValidation(Validator $validator) { throw new HttpResponseException( response()->json([ 'message' => 'Validation Error', 'errors' => $validator->errors() ], 422) ); } }
方法2:全局异常处理器自定义ValidationException响应
修改全局异常处理类,针对JSON请求统一修改ValidationException的响应格式:
Laravel 8/9版本(app/Exceptions/Handler.php)
<?php namespace App\Exceptions; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Validation\ValidationException; use Throwable; class Handler extends ExceptionHandler { // 其他代码保持不变 public function render($request, Throwable $exception) { // 仅对JSON请求处理ValidationException if ($exception instanceof ValidationException && $request->wantsJson()) { return response()->json([ 'message' => 'Validation Error', 'errors' => $exception->errors() ], 422); } return parent::render($request, $exception); } }
Laravel 10+版本(app/Exceptions/Handler.php)
在register方法中添加可渲染的异常回调:
<?php namespace App\Exceptions; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Validation\ValidationException; use Throwable; class Handler extends ExceptionHandler { // 其他代码保持不变 public function register() { $this->renderable(function (ValidationException $e, $request) { if ($request->wantsJson()) { return response()->json([ 'message' => 'Validation Error', 'errors' => $e->errors() ], 422); } }); } }
方法3:控制器内手动验证(放弃FormRequest)
如果一定要在控制器的try-catch中处理验证,可以直接使用Validator类手动验证,绕过FormRequest的自动验证机制:
<?php namespace App\Http\Controllers; use App\Models\Document; use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; class DocumentController extends Controller { public function store(Request $req) { try { // 手动执行验证 $validData = Validator::make($req->all(), [ "citizen_id"=>["required", "exists:citizens,id"], "document_file"=>["required", "image:png"], "type_id"=>["required", new \App\Rules\OnlyOneTypeOfDocument($req->citizen_id)] ])->validate(); $path = $this->storeImage($req->file('document_file')); $validData['doc_path'] = $path; unset($validData['document_file']); Document::create($validData); return response()->json([ "message"=>"Add document for citizen $validData[nik] is success", "data"=>$validData ], 201); } catch(ValidationException $e) { return response()->json([ "message"=>"Validation Error", "errors"=>$e->errors() ], 422); } catch(\Exception $e) { return response()->json([ 'message' => "Internal Server Error : \n".$e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => $e->getTrace(), ], 500); } } private function storeImage($file) { $file_name = time() . '-' . uniqid() . '.' . $file->getClientOriginalExtension(); $path = "public/images"; $file_path = $file->storeAs($path, $file_name); return $file_path; } }
说明
- 方法1是最优解,因为FormRequest的设计初衷就是封装验证逻辑,将验证失败的响应逻辑放在Request类中更符合单一职责原则。
- 方法2适合需要全局统一验证响应格式的场景。
- 方法3仅在特殊需求下使用,会导致控制器代码冗余,不推荐常规使用。
内容的提问来源于stack exchange,提问作者Felix
相关产品推荐
相关产品推荐

