A simple library to force and make consistent development REST API controllers. It allows to define structure of the payload and it validator constraints. It also provides much easier way to access validated data from within the controller body.
composer require pln0w/rest-api-validator
Add following section to your services.yml
Pawly\RestApiValidator\Resolver\CustomRequestResolver:
arguments:
- '@validator'
tags:
- { name: controller.request_value_resolver }
Each request object must have request_stack service injected, i.e:
User\Infrastructure\Request\RegisterUserRequest:
arguments:
- '@request_stack'
-
Steps to provide validation to your controllers:
- Create custom request overriding Pawly\RestApiValidator\Request\AbstractCustomRequest
- Define request class properties that you want to map request values with
- Add validation constraints to $metadata (no yaml, no addnotations - pure PHP config)
- (optional) Override getters for properties in needed or add constraint explicit against getter
See validation docs: https://symfony.com/doc/master/validation.html
<?php declare(strict_types=1); namespace User\Infrastructure\Request; use Pawly\RestApiValidator\Request\AbstractCustomRequest; use Symfony\Component\Validator\Constraints as Assert; class RegisterUserRequest extends AbstractCustomRequest { protected ?string $email = null; protected ?string $password = null; /** * @inheritDoc */ public function getValidationRules() { return new Assert\Collection([ 'email' => new Assert\Email(), 'password' => [ new Assert\NotBlank(), new Assert\Length(['min' => 6]) ] ]); } }
- Create controller having this request injected
<?php declare(strict_types=1); namespace User\Infrastructure\Controller; use Pawly\RestApiValidator\Response\ApiResponse; use User\Infrastructure\Request\RegisterUserRequest; final class RegisterUserAction { public function __invoke(RegisterUserRequest $request): ApiResponse { // do some stuff ... $email = $request->getEmail(); return ApiResponse::json(['email' => $email], ApiResponse::HTTP_OK); } }
<?php declare(strict_types=1); namespace User\Infrastructure\Request; use Pawly\RestApiValidator\Request\AbstractCustomRequest; use Symfony\Component\Validator\Constraints as Assert; use Shared\Domain\ValueObject\Email; class RegisterUserRequest extends AbstractCustomRequest { protected ?string $email = null; protected ?string $password = null; /** * @inheritDoc */ public function getValidationRules() { return new Assert\Collection([ 'email' => new Assert\Email(), 'password' => [ new Assert\NotBlank(), new Assert\Length(['min' => 6]) ] ]); } public function getEmail(): Email { return new Email($this->email); } }
-
Validation errors handling
If request has invalid data, then exception is thrown and handled in subscriber, that will provide following message structure:
{
"status": 422,
"message": "Request validation error",
"details": {
"email": "This value should not be correct email address.",
"password": "This value should not be blank."
}
}
If you want to ensure you use unified response for controllers, you can enable checking response interface by addint below definition to your services.yaml
Pawly\RestApiValidator\Subscriber\ValidateResponseInterfaceSubscriber:
tags:
- { name: kernel.event_subscriber, event: kernel.response }