diff --git a/src/Http/FormRequest.php b/src/Http/FormRequest.php index 646bf70..d4839c8 100644 --- a/src/Http/FormRequest.php +++ b/src/Http/FormRequest.php @@ -2,17 +2,145 @@ namespace Dingo\Api\Http; +use Illuminate\Http\Request; +use Illuminate\Http\JsonResponse; +use Laravel\Lumen\Http\Redirector; +use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Validation\Validator; use Dingo\Api\Exception\ValidationHttpException; +use Illuminate\Validation\ValidatesWhenResolvedTrait; use Symfony\Component\HttpKernel\Exception\HttpException; -use Illuminate\Foundation\Http\FormRequest as IlluminateFormRequest; +use Illuminate\Contracts\Validation\ValidatesWhenResolved; +use Illuminate\Contracts\Validation\Factory as ValidationFactory; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; -class FormRequest extends IlluminateFormRequest +class FormRequest extends Request implements ValidatesWhenResolved { + use ValidatesWhenResolvedTrait; + + /** + * The container instance. + * + * @var \Illuminate\Contracts\Container\Container + */ + protected $container; + + /** + * The redirector instance. + * + * @var \Illuminate\Routing\Redirector + */ + protected $redirector; + + /** + * The URI to redirect to if validation fails. + * + * @var string + */ + protected $redirect; + + /** + * The route to redirect to if validation fails. + * + * @var string + */ + protected $redirectRoute; + + /** + * The controller action to redirect to if validation fails. + * + * @var string + */ + protected $redirectAction; + + /** + * The key to be used for the view error bag. + * + * @var string + */ + protected $errorBag = 'default'; + + /** + * The input keys that should not be flashed on redirect. + * + * @var array + */ + protected $dontFlash = [ + 'password', + 'password_confirmation', + ]; + + /** + * Validate the request. + */ + public function validate() + { + if ($this->authorize() === false) { + throw new AccessDeniedHttpException(); + } + + $validator = app('validator')->make($this->all(), $this->rules(), $this->messages()); + + if ($validator->fails()) { + throw new ValidationHttpException($validator->errors()); + } + } + + /** + * Get the validator instance for the request. + * + * @return \Illuminate\Contracts\Validation\Validator + * + * @SuppressWarnings(PHPMD.ElseExpression) + */ + protected function getValidatorInstance() + { + $factory = $this->container->make(ValidationFactory::class); + + if (method_exists($this, 'validator')) { + $validator = $this->container->call([$this, 'validator'], compact('factory')); + } else { + $validator = $this->createDefaultValidator($factory); + } + + if (method_exists($this, 'withValidator')) { + $this->withValidator($validator); + } + + return $validator; + } + + /** + * Create the default validator instance. + * + * @param \Illuminate\Contracts\Validation\Factory $factory + * + * @return \Illuminate\Contracts\Validation\Validator + */ + protected function createDefaultValidator(ValidationFactory $factory) + { + return $factory->make( + $this->validationData(), + $this->container->call([$this, 'rules']), + $this->messages(), + $this->attributes() + ); + } + + /** + * Get data to be validated from the request. + * + * @return array + */ + protected function validationData() + { + return $this->all(); + } + /** * Handle a failed validation attempt. * - * @param \Illuminate\Contracts\Validation\Validator $validator + * @param \Illuminate\Contracts\Validation\Validator $validator * * @return void */ @@ -25,6 +153,70 @@ protected function failedValidation(Validator $validator) parent::failedValidation($validator); } + /** + * Get the proper failed validation response for the request. + * + * @param array $errors + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function response(array $errors) + { + if ($this->expectsJson()) { + return new JsonResponse($errors, 422); + } + + return $this->redirector->to($this->getRedirectUrl()) + ->withInput($this->except($this->dontFlash)) + ->withErrors($errors, $this->errorBag); + } + + /** + * Format the errors from the given Validator instance. + * + * @param \Illuminate\Contracts\Validation\Validator $validator + * + * @return array + */ + protected function formatErrors(Validator $validator) + { + return $validator->getMessageBag()->toArray(); + } + + /** + * Get the URL to redirect to on a validation error. + * + * @return string + */ + protected function getRedirectUrl() + { + $url = $this->redirector->getUrlGenerator(); + + if ($this->redirect) { + return $url->to($this->redirect); + } elseif ($this->redirectRoute) { + return $url->route($this->redirectRoute); + } elseif ($this->redirectAction) { + return $url->action($this->redirectAction); + } + + return $url->previous(); + } + + /** + * Determine if the request passes the authorization check. + * + * @return bool + */ + protected function passesAuthorization() + { + if (method_exists($this, 'authorize')) { + return $this->container->call([$this, 'authorize']); + } + + return false; + } + /** * Handle a failed authorization attempt. * @@ -38,4 +230,52 @@ protected function failedAuthorization() parent::failedAuthorization(); } + + /** + * Get custom messages for validator errors. + * + * @return array + */ + public function messages() + { + return []; + } + + /** + * Get custom attributes for validator errors. + * + * @return array + */ + public function attributes() + { + return []; + } + + /** + * Set the Redirector instance. + * + * @param Redirector $redirector + * + * @return $this + */ + public function setRedirector(Redirector $redirector) + { + $this->redirector = $redirector; + + return $this; + } + + /** + * Set the container implementation. + * + * @param \Illuminate\Contracts\Container\Container $container + * + * @return $this + */ + public function setContainer(Container $container) + { + $this->container = $container; + + return $this; + } } diff --git a/src/Provider/LumenServiceProvider.php b/src/Provider/LumenServiceProvider.php index 3b71381..b1558ad 100644 --- a/src/Provider/LumenServiceProvider.php +++ b/src/Provider/LumenServiceProvider.php @@ -3,6 +3,9 @@ namespace Dingo\Api\Provider; use ReflectionClass; +use Laravel\Lumen\Application; +use Dingo\Api\Http\FormRequest; +use Laravel\Lumen\Http\Redirector; use Dingo\Api\Http\Middleware\Auth; use Dingo\Api\Http\Middleware\Request; use Dingo\Api\Http\Middleware\RateLimit; @@ -11,6 +14,7 @@ use FastRoute\RouteParser\Std as StdRouteParser; use Illuminate\Http\Request as IlluminateRequest; use Dingo\Api\Routing\Adapter\Lumen as LumenAdapter; +use Illuminate\Contracts\Validation\ValidatesWhenResolved; use FastRoute\DataGenerator\GroupCountBased as GcbDataGenerator; class LumenServiceProvider extends DingoServiceProvider @@ -18,6 +22,8 @@ class LumenServiceProvider extends DingoServiceProvider /** * Boot the service provider. * + * @throws \ReflectionException + * * @return void */ public function boot() @@ -48,6 +54,16 @@ public function boot() }); }); + $this->app->afterResolving(ValidatesWhenResolved::class, function ($resolved) { + $resolved->validate(); + }); + + $this->app->resolving(FormRequest::class, function (FormRequest $request, Application $app) { + $this->initializeRequest($request, $app['request']); + + $request->setContainer($app)->setRedirector($app->make(Redirector::class)); + }); + $this->app->routeMiddleware([ 'api.auth' => Auth::class, 'api.throttle' => RateLimit::class, @@ -131,4 +147,39 @@ protected function gatherAppMiddleware(ReflectionClass $reflection) return $middleware; } + + /** + * Initialize the form request with data from the given request. + * + * @param FormRequest $form + * @param IlluminateRequest $current + * + * @return void + */ + protected function initializeRequest(FormRequest $form, IlluminateRequest $current) + { + $files = $current->files->all(); + + $files = is_array($files) ? array_filter($files) : $files; + + $form->initialize( + $current->query->all(), + $current->request->all(), + $current->attributes->all(), + $current->cookies->all(), + $files, + $current->server->all(), + $current->getContent() + ); + + $form->setJson($current->json()); + + if ($session = $current->getSession()) { + $form->setLaravelSession($session); + } + + $form->setUserResolver($current->getUserResolver()); + + $form->setRouteResolver($current->getRouteResolver()); + } } diff --git a/src/Routing/Route.php b/src/Routing/Route.php index 308b801..3a70032 100644 --- a/src/Routing/Route.php +++ b/src/Routing/Route.php @@ -533,4 +533,14 @@ public function secure() { return in_array('https', $this->action, true); } + + /** + * Return the middlewares for this route. + * + * @return array + */ + public function getMiddleware() + { + return $this->middleware; + } }