Laravel 10跨控制器调用FormRequest触发validated()空对象错误
Laravel 10控制器间调用时
validated()报null错误解决 问题场景
使用Laravel 10,尝试从AdminV1Controller调用AdminController的store方法时,AdminController执行到$validatedData = $request->validated();时抛出错误:Call to a member function validated() on null。
相关代码
AdminV1Controller代码
class AdminV1Controller extends Controller { public function store(AdminV1Request $request): \Illuminate\Http\JsonResponse { try { $validatedData = $request->validated(); return (new AdminController)->store(new StoreEventRequest($validatedData)); } catch (\Exception $e) { Log::error('Failed to store event: ' , ["error"=>$e->getMessage()]); return response()->json(['message' => 'Failed to store event', 'error' => $e->getMessage()], 500); } } }
AdminController代码
class AdminController extends Controller { public function store(StoreEventRequest $request) { try { $validatedData = $request->validated(); $event = Event::createEvent($validatedData); return response() ->json(['message' => 'Event created successfully', 'data' => new EventResource($event)], 201); } catch (\Exception $e) { Log::error('Failed to create event: ' , ["error" => $e->getMessage()] ); return response()->json(['message' => 'Failed to create event', 'error' => $e->getMessage()], 500); } } }
AdminV1Request验证规则
<?php namespace App\Http\Requests\Admin; use App\Enums\ValidationEnum; use Illuminate\Contracts\Validation\Validator; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Http\Exceptions\HttpResponseException; class AdminV1Request extends FormRequest { public function authorize(): bool { return true; } public function rules(): array { return [ 'name' => 'required|string|max:255', 'description' => 'required|string', 'date' => 'required|date|date_format:Y-m-d', 'location' => 'required|string|max:255', ]; } public function failedValidation(Validator $validator): void { throw new HttpResponseException(response()->json([ 'success' => false, 'message' => 'Validation errors', 'data' => $validator->errors() ])); } public function messages(): array { return [ 'name.required' => ValidationEnum::REQUIRED_MESSAGE->value, 'name.string' => ValidationEnum::STRING_MESSAGE->value, 'name.max' => 'The :attribute may not be greater than :max characters.', 'description.required' => ValidationEnum::REQUIRED_MESSAGE->value, 'description.string' => ValidationEnum::STRING_MESSAGE->value, 'date.required' => ValidationEnum::REQUIRED_MESSAGE->value, 'date.date' => 'The :attribute must be a valid date.', 'date.date_format' => 'The :attribute must be a YYYY-MM-DD format.', 'location.required' => ValidationEnum::REQUIRED_MESSAGE->value, 'location.string' => ValidationEnum::STRING_MESSAGE->value, 'location.max' => 'The :attribute may not be greater than :max characters.', ]; } }
StoreEventRequest验证规则
<?php namespace App\Http\Requests\Admin; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Contracts\Validation\Validator; use Illuminate\Http\Exceptions\HttpResponseException; class StoreEventRequest extends FormRequest { public function authorize() { return true; } public function rules() { return [ 'name' => 'nullable|string|max:255', 'description' => 'nullable|string', 'date' => 'nullable|date|date_format:Y-m-d', 'location' => 'nullable|string|max:255', ]; } public function failedValidation(Validator $validator) { throw new HttpResponseException(response()->json([ 'success' => false, 'message' => 'Validation errors', 'data' => $validator->errors() ])); } public function messages() { return [ 'name.string' => 'The :attribute must be a string.', 'name.max' => 'The :attribute may not be greater than :max characters.', 'description.string' => 'The :attribute must be a string.', 'date.date' => 'The :attribute must be a valid date.', 'date.date_format' => 'The :attribute must be a YYYY-MM-DD format.', 'location.string' => 'The :attribute must be a string.', 'location.max' => 'The :attribute may not be greater than :max characters.', ]; } }
路由配置(api.php)
<?php Route::controller(Admin::class) ->prefix('admin') ->middleware(['auth:api']) ->group(function () { Route::post('/event', 'store'); Route::put('/event/{id}', 'update')->where('id', '[0-9]+'); Route::delete('/event/{id}', 'destroy')->where('id', '[0-9]+'); Route::get('/events', 'index'); }); Route::controller(AdminV1::class) ->prefix('admin/v1') ->middleware(['auth:api']) ->group(function () { Route::post('/event', 'store'); Route::put('/event/{id}', 'update')->where('id', '[0-9]+'); Route::delete('/event/{id}', 'destroy')->where('id', '[0-9]+'); Route::get('/events', 'index'); });
错误原因
Laravel的FormRequest是依赖容器解析的,框架会自动处理请求数据填充、验证逻辑等流程。直接通过new StoreEventRequest($validatedData)实例化时,没有经过框架的初始化流程,FormRequest内部的请求数据、验证状态等属性未正确设置,导致调用validated()方法时出现空指针错误。
解决方案
方案1:手动初始化FormRequest并触发验证
在AdminV1Controller中,不要直接newStoreEventRequest,而是通过create方法初始化请求数据,并手动触发验证:
public function store(AdminV1Request $request): \Illuminate\Http\JsonResponse { try { $validatedData = $request->validated(); // 初始化StoreEventRequest,传递当前请求的上下文和验证后的数据 $storeRequest = StoreEventRequest::create( $request->url(), $request->method(), $validatedData, $request->headers->all(), $request->allFiles() ); // 手动触发验证流程 $storeRequest->validateResolved(); return (new AdminController)->store($storeRequest); } catch (\Exception $e) { Log::error('Failed to store event: ', ["error" => $e->getMessage()]); return response()->json(['message' => 'Failed to store event', 'error' => $e->getMessage()], 500); } }
方案2:重构业务逻辑到Service层(推荐)
控制器的职责应该是处理请求和响应,业务逻辑抽离到Service层可以避免控制器间的耦合,同时复用验证逻辑:
- 创建EventService类:
// app/Services/EventService.php namespace App\Services; use App\Models\Event; use App\Http\Requests\Admin\StoreEventRequest; use Illuminate\Support\Facades\Validator; class EventService { public function createEvent(array $data) { // 复用StoreEventRequest的验证规则 $validator = Validator::make($data, (new StoreEventRequest)->rules()); // 触发验证,失败则抛出异常 $validator->validate(); return Event::createEvent($validator->validated()); } }
- 修改AdminController的store方法:
public function store(StoreEventRequest $request) { try { $event = app(\App\Services\EventService::class)->createEvent($request->validated()); return response() ->json(['message' => 'Event created successfully', 'data' => new EventResource($event)], 201); } catch (\Exception $e) { Log::error('Failed to create event: ', ["error" => $e->getMessage()]); return response()->json(['message' => 'Failed to create event', 'error' => $e->getMessage()], 500); } }
- 修改AdminV1Controller的store方法:
public function store(AdminV1Request $request): \Illuminate\Http\JsonResponse { try { $event = app(\App\Services\EventService::class)->createEvent($request->validated()); return response() ->json(['message' => 'Event created successfully', 'data' => new EventResource($event)], 201); } catch (\Exception $e) { Log::error('Failed to store event: ', ["error" => $e->getMessage()]); return response()->json(['message' => 'Failed to store event', 'error' => $e->getMessage()], 500); } }
这种方式既保证了StoreEventRequest的验证规则被复用,又消除了控制器之间的依赖,符合Laravel的最佳实践。
内容的提问来源于stack exchange,提问作者CodeLove
相关产品推荐
相关产品推荐

