diff --git a/core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php b/core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php index cefe7438201b838fd83b3a10f6a1f68b3e1c72e6..18655727ba628a42070a0f75ef18029f19b96d47 100644 --- a/core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php +++ b/core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php @@ -101,18 +101,18 @@ protected function assertNormalizationEdgeCases($method, Url $url, array $reques $normalization['_links']['type'] = Url::fromUri('base:rest/type/' . static::$entityTypeId . '/bad_bundle_name'); $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); - // DX: 400 when incorrect entity type bundle is specified. + // DX: 422 when incorrect entity type bundle is specified. $response = $this->request($method, $url, $request_options); - $this->assertResourceErrorResponse(400, 'No entity type(s) specified', $response); + $this->assertResourceErrorResponse(422, 'No entity type(s) specified', $response); unset($normalization['_links']['type']); $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); - // DX: 400 when no entity type bundle is specified. + // DX: 422 when no entity type bundle is specified. $response = $this->request($method, $url, $request_options); - $this->assertResourceErrorResponse(400, 'The type link relation must be specified.', $response); + $this->assertResourceErrorResponse(422, 'The type link relation must be specified.', $response); } } diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php index 0e9f41bbab484ebcad61efe3a1686d3819e5aca5..d7a6be71afb6270611e0fef8e97a00a4017cc39c 100644 --- a/core/modules/rest/src/RequestHandler.php +++ b/core/modules/rest/src/RequestHandler.php @@ -11,8 +11,10 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException; use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; use Symfony\Component\Serializer\Exception\UnexpectedValueException; +use Symfony\Component\Serializer\Exception\InvalidArgumentException; /** * Acts as intermediate request forwarder for resource plugins. @@ -96,19 +98,32 @@ public function handle(RouteMatchInterface $route_match, Request $request) { $request_method = $request->getMethod(); if (in_array($format, $resource_config->getFormats($request_method))) { $definition = $resource->getPluginDefinition(); + + // First decode the request data. We can then determine if the + // serialized data was malformed. try { - if (!empty($definition['serialization_class'])) { - $unserialized = $serializer->deserialize($received, $definition['serialization_class'], $format, array('request_method' => $method)); - } - // If the plugin does not specify a serialization class just decode - // the received data. - else { - $unserialized = $serializer->decode($received, $format, array('request_method' => $method)); - } + $unserialized = $serializer->decode($received, $format, ['request_method' => $method]); } catch (UnexpectedValueException $e) { + // If an exception was thrown at this stage, there was a problem + // decoding the data. Throw a 400 http exception. throw new BadRequestHttpException($e->getMessage()); } + + // Then attempt to denormalize if there is a serialization class. + if (!empty($definition['serialization_class'])) { + try { + $unserialized = $serializer->denormalize($unserialized, $definition['serialization_class'], $format, ['request_method' => $method]); + } + // These two serialization exception types mean there was a problem + // with the structure of the decoded data and it's not valid. + catch (UnexpectedValueException $e) { + throw new UnprocessableEntityHttpException($e->getMessage()); + } + catch (InvalidArgumentException $e) { + throw new UnprocessableEntityHttpException($e->getMessage()); + } + } } else { throw new UnsupportedMediaTypeHttpException(); diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index 73dfc628daae5160540ace51e97ce0b495dec05b..2759bc66fa221fad2088f84fecf449e52e374893 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@ -1035,10 +1035,9 @@ protected function assertNormalizationEdgeCases($method, Url $url, array $reques $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); - // DX: 400 when incorrect entity type bundle is specified. - // @todo Change to 422 in https://www.drupal.org/node/2827084. + // DX: 422 when incorrect entity type bundle is specified. $response = $this->request($method, $url, $request_options); - $this->assertResourceErrorResponse(400, '"bad_bundle_name" is not a valid bundle type for denormalization.', $response); + $this->assertResourceErrorResponse(422, '"bad_bundle_name" is not a valid bundle type for denormalization.', $response); } @@ -1046,10 +1045,9 @@ protected function assertNormalizationEdgeCases($method, Url $url, array $reques $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); - // DX: 400 when no entity type bundle is specified. - // @todo Change to 422 in https://www.drupal.org/node/2827084. + // DX: 422 when no entity type bundle is specified. $response = $this->request($method, $url, $request_options); - $this->assertResourceErrorResponse(400, sprintf('Could not determine entity type bundle: "%s" field is missing.', $bundle_field_name), $response); + $this->assertResourceErrorResponse(422, sprintf('Could not determine entity type bundle: "%s" field is missing.', $bundle_field_name), $response); } }