diff --cc core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php index cefe743,5576b2b..0000000 --- a/core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php +++ b/core/modules/hal/tests/src/Functional/EntityResource/HalEntityNormalizationTrait.php @@@ -101,18 -101,26 +101,18 @@@ trait HalEntityNormalizationTrait $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); - // @todo Uncomment, remove next 3 in https://www.drupal.org/node/2813853. - // $this->assertResourceErrorResponse(400, 'No entity type(s) specified', $response); - $this->assertSame(400, $response->getStatusCode()); - $this->assertSame([static::$mimeType], $response->getHeader('Content-Type')); - $this->assertSame($this->serializer->encode(['error' => 'No entity type(s) specified'], static::$format), (string) $response->getBody()); ++ $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); - // @todo Uncomment, remove next 3 in https://www.drupal.org/node/2813853. - // $this->assertResourceErrorResponse(422, 'The type link relation must be specified.', $response); - $this->assertSame(422, $response->getStatusCode()); - $this->assertSame([static::$mimeType], $response->getHeader('Content-Type')); - $this->assertSame($this->serializer->encode(['error' => 'The type link relation must be specified.'], static::$format), (string) $response->getBody()); ++ $this->assertResourceErrorResponse(422, 'The type link relation must be specified.', $response); } } diff --cc core/modules/rest/src/RequestHandler.php index 0e9f41b,8857c2e..0000000 --- a/core/modules/rest/src/RequestHandler.php +++ b/core/modules/rest/src/RequestHandler.php @@@ -10,9 -10,10 +10,11 @@@ use Symfony\Component\DependencyInjecti use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Exception\HttpException; +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 -97,32 +98,32 @@@ class RequestHandler implements Contain $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. - $this->throwHttpException(400, $e, $request); + 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) { - $this->throwHttpException(422, $e, $request); ++ throw new UnprocessableEntityHttpException($e->getMessage()); + } + catch (InvalidArgumentException $e) { - $this->throwHttpException(422, $e, $request); ++ throw new UnprocessableEntityHttpException($e->getMessage()); + } + } } else { throw new UnsupportedMediaTypeHttpException(); diff --cc core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index e9d9c48,88fed50..0000000 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@@ -960,10 -965,13 +960,9 @@@ abstract class EntityResourceTestBase e $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); - // @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813853 lands. - // $this->assertResourceErrorResponse(422, '"bad_bundle_name" is not a valid bundle type for denormalization.', $response); - $this->assertSame(422, $response->getStatusCode()); - $this->assertSame([static::$mimeType], $response->getHeader('Content-Type')); - $this->assertSame($this->serializer->encode(['error' => '"bad_bundle_name" is not a valid bundle type for denormalization.'], static::$format), (string) $response->getBody()); ++ $this->assertResourceErrorResponse(422, '"bad_bundle_name" is not a valid bundle type for denormalization.', $response); } @@@ -971,10 -979,13 +970,9 @@@ $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, 'A string must be provided as a bundle value.', $response); - // @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813853 lands. - // $this->assertResourceErrorResponse(422, 'A string must be provided as a bundle value.', $response); - $this->assertSame(422, $response->getStatusCode()); - $this->assertSame([static::$mimeType], $response->getHeader('Content-Type')); - $this->assertSame($this->serializer->encode(['error' => 'A string must be provided as a bundle value.'], static::$format), (string) $response->getBody()); ++ $this->assertResourceErrorResponse(422, 'A string must be provided as a bundle value.', $response); } }