The OpenAPI Command Bundle integrates with the Symfony Validator component to ensure your command DTOs are valid before they reach your message handlers. It also provides a robust error-handling mechanism that returns RFC 7807-compliant Problem Details responses.
By default, the bundle automatically validates every command deserialized from a request. This happens within the CommandController before the command is dispatched to the Messenger bus.
Simply use standard Symfony Validator constraints on your command properties:
namespace App\Command;
use OpenApi\Attributes as OA;
use Symfony\Component\Validator\Constraints as Assert;
#[OA\Post(path: '/api/users', operationId: 'create_user')]
final class CreateUserCommand
{
public function __construct(
#[Assert\NotBlank]
#[Assert\Email]
public string $email,
#[Assert\NotBlank]
#[Assert\Length(min: 8)]
public string $password,
) {}
}If a request fails validation, the bundle will interrupt the flow and return a 400 Bad Request response with violation details.
You can customize the validation behavior in config/packages/stixx_openapi_command.yaml:
stixx_openapi_command:
validation:
enabled: true # Enable or disable automatic validation (default: true)
groups: ['Default'] # Specify validation groups to use (default: ['Default'])If you need to use specific validation groups, you can configure them globally in the bundle configuration as shown above.
Note: Currently, validation groups are applied globally to all commands handled by the bundle.
When an error occurs (validation fail, malformed JSON, mapping error, etc.), the bundle returns a response using the RFC 7807 (Problem Details for HTTP APIs) standard.
Problem responses use the application/problem+json media type.
{
"type": "about:blank",
"title": "Validation failed",
"status": 400,
"detail": "Validation failed",
"violations": [
{
"propertyPath": "email",
"title": "This value is not a valid email address.",
"parameters": {
"{{ value }}": "\"invalid-email\""
},
"type": "urn:uuid:bd79c0ab-ddb3-4675-903c-8b6141c2f08b"
}
]
}If the request body cannot be mapped to your command DTO (e.g., missing required properties in the JSON, type mismatch), the bundle throws a BadRequestHttpException which is transformed into a Problem Details response:
{
"type": "about:blank",
"title": "Unable to map request to command",
"status": 400,
"detail": "Unable to map request to command: Required parameter \"name\" is missing"
}If you prefer to handle errors yourself or don't want to use the Problem Details format, you can disable it:
stixx_openapi_command:
openapi:
problem_details: falseWhen disabled, the bundle will not prepend Problem Details models to your NelmioApiDoc configuration, but it will still throw exceptions that you can catch in your own event listeners.
In addition to standard DTO validation, you can extend the validation of the incoming HTTP request by implementing custom request validators. This is useful for cross-field validation, checking headers, or performing security checks before the command is even deserialized.
Create a class that implements Stixx\OpenApiCommandBundle\Validator\ValidatorInterface:
namespace App\Validator;
use Stixx\OpenApiCommandBundle\Validator\ValidatorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
final class CustomHeaderValidator implements ValidatorInterface
{
public function validate(Request $request): void
{
if (!$request->headers->has('X-Custom-Header')) {
throw new BadRequestHttpException('Missing X-Custom-Header');
}
}
}Register your validator as a service and tag it with stixx_openapi_command.request.validator. If you have autoconfiguration enabled, the bundle will automatically detect and register your validator if it implements the interface.
# config/services.yaml
services:
App\Validator\CustomHeaderValidator:
tags:
- { name: 'stixx_openapi_command.request.validator' }All tagged validators are executed in a chain during the kernel.request event, but only for routes that are managed by this bundle (detected via NelmioAreaRoutesChecker). If any validator throws an exception, the request cycle is interrupted.
The bundle uses an ExceptionToApiProblemTransformer to convert internal exceptions into ApiProblemException. You can decorate or override this service if you need to customize how specific exceptions are mapped to problem details.