Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion features/graphql/mutation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ Feature: GraphQL mutation support
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors[0].extensions.status" should be equal to "400"
And the JSON node "errors[0].extensions.status" should be equal to "422"
And the JSON node "errors[0].message" should be equal to "name: This value should not be blank."
And the JSON node "errors[0].extensions.violations" should exist
And the JSON node "errors[0].extensions.violations[0].path" should be equal to "name"
Expand Down
2 changes: 1 addition & 1 deletion features/hal/problem.feature
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Feature: Error handling valid according to RFC 7807 (application/problem+json)
"""
{}
"""
Then the response status code should be 400
Then the response status code should be 422
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON should be equal to:
Expand Down
2 changes: 1 addition & 1 deletion features/hydra/error.feature
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Feature: Error handling
"""
{}
"""
Then the response status code should be 400
Then the response status code should be 422
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
Expand Down
4 changes: 2 additions & 2 deletions features/jsonapi/errors.feature
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Feature: JSON API error handling
}
}
"""
Then the response status code should be 400
Then the response status code should be 422
And the response should be in JSON
And the JSON should be valid according to the JSON API schema
And the JSON should be equal to:
Expand Down Expand Up @@ -49,7 +49,7 @@ Feature: JSON API error handling
}
}
"""
Then the response status code should be 400
Then the response status code should be 422
And the response should be in JSON
And the JSON should be valid according to the JSON API schema
And the JSON should be equal to:
Expand Down
4 changes: 2 additions & 2 deletions features/main/validation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Feature: Using validations groups
"code": "My Dummy"
}
"""
Then the response status code should be 400
Then the response status code should be 422
And the response should be in JSON
And the JSON should be equal to:
"""
Expand Down Expand Up @@ -52,7 +52,7 @@ Feature: Using validations groups
"code": "My Dummy"
}
"""
Then the response status code should be 400
Then the response status code should be 422
And the response should be in JSON
And the JSON should be equal to:
"""
Expand Down
2 changes: 1 addition & 1 deletion features/security/send_security_headers.feature
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Feature: Send security header
"""
{"name": ""}
"""
Then the response status code should be 400
Then the response status code should be 422
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the header "X-Content-Type-Options" should be equal to "nosniff"
And the header "X-Frame-Options" should be equal to "deny"
1 change: 1 addition & 0 deletions src/Bridge/Symfony/Bundle/Resources/config/api.xml
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@
<service id="api_platform.listener.exception.validation" class="ApiPlatform\Core\Bridge\Symfony\Validator\EventListener\ValidationExceptionListener">
<argument type="service" id="api_platform.serializer" />
<argument>%api_platform.error_formats%</argument>
<argument>%api_platform.exception_to_status%</argument>

<tag name="kernel.event_listener" event="kernel.exception" method="onKernelException" />
</service>
Expand Down
2 changes: 2 additions & 0 deletions src/Bridge/Symfony/Bundle/Resources/config/graphql.xml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@
</service>

<service id="api_platform.graphql.normalizer.validation_exception" class="ApiPlatform\Core\GraphQl\Serializer\Exception\ValidationExceptionNormalizer">
<argument>%api_platform.exception_to_status%</argument>

<tag name="serializer.normalizer" priority="-780" />
</service>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ final class ValidationExceptionListener
{
private $serializer;
private $errorFormats;
private $exceptionToStatus;

public function __construct(SerializerInterface $serializer, array $errorFormats)
public function __construct(SerializerInterface $serializer, array $errorFormats, array $exceptionToStatus = [])
{
$this->serializer = $serializer;
$this->errorFormats = $errorFormats;
$this->exceptionToStatus = $exceptionToStatus;
}

/**
Expand All @@ -44,12 +46,22 @@ public function onKernelException(ExceptionEvent $event): void
if (!$exception instanceof ValidationException) {
return;
}
$exceptionClass = \get_class($exception);
$statusCode = Response::HTTP_UNPROCESSABLE_ENTITY;

foreach ($this->exceptionToStatus as $class => $status) {
if (is_a($exceptionClass, $class, true)) {
$statusCode = $status;

break;
}
}

$format = ErrorFormatGuesser::guessErrorFormat($event->getRequest(), $this->errorFormats);

$event->setResponse(new Response(
$this->serializer->serialize($exception->getConstraintViolationList(), $format['key']),
Response::HTTP_BAD_REQUEST,
$statusCode,
[
'Content-Type' => sprintf('%s; charset=utf-8', $format['value'][0]),
'X-Content-Type-Options' => 'nosniff',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@
*/
final class ValidationExceptionNormalizer implements NormalizerInterface
{
private $exceptionToStatus;

public function __construct(array $exceptionToStatus = [])
{
$this->exceptionToStatus = $exceptionToStatus;
}

/**
* {@inheritdoc}
*/
Expand All @@ -39,7 +46,18 @@ public function normalize($object, $format = null, array $context = []): array
$validationException = $object->getPrevious();
$error = FormattedError::createFromException($object);
$error['message'] = $validationException->getMessage();
$error['extensions']['status'] = Response::HTTP_BAD_REQUEST;

$exceptionClass = \get_class($validationException);
$statusCode = Response::HTTP_UNPROCESSABLE_ENTITY;

foreach ($this->exceptionToStatus as $class => $status) {
if (is_a($exceptionClass, $class, true)) {
$statusCode = $status;

break;
}
}
$error['extensions']['status'] = $statusCode;
$error['extensions']['category'] = 'user';
$error['extensions']['violations'] = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function testValidationException()
$response = $event->getResponse();
$this->assertInstanceOf(Response::class, $response);
$this->assertSame($exceptionJson, $response->getContent());
$this->assertSame(Response::HTTP_BAD_REQUEST, $response->getStatusCode());
$this->assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response->getStatusCode());
$this->assertSame('application/ld+json; charset=utf-8', $response->headers->get('Content-Type'));
$this->assertSame('nosniff', $response->headers->get('X-Content-Type-Options'));
$this->assertSame('deny', $response->headers->get('X-Frame-Options'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function testNormalize(): void

$normalizedError = $this->validationExceptionNormalizer->normalize($error);
$this->assertSame($exceptionMessage, $normalizedError['message']);
$this->assertSame(400, $normalizedError['extensions']['status']);
$this->assertSame(422, $normalizedError['extensions']['status']);
$this->assertSame('user', $normalizedError['extensions']['category']);
$this->assertArrayHasKey('violations', $normalizedError['extensions']);
$this->assertSame([
Expand Down