diff --git a/core/modules/rest/src/Plugin/ResourceBase.php b/core/modules/rest/src/Plugin/ResourceBase.php index 3062aa29ed8b4b3cbb3ac8b9a5425ad42289122a..50d8d758ced5a4b9fb8965dd6c5993f2a655459b 100644 --- a/core/modules/rest/src/Plugin/ResourceBase.php +++ b/core/modules/rest/src/Plugin/ResourceBase.php @@ -111,16 +111,6 @@ public function routes() { switch ($method) { case 'POST': $route->setPath($create_path); - // Restrict the incoming HTTP Content-type header to the known - // serialization formats. - $route->addRequirements(['_content_type_format' => implode('|', $this->serializerFormats)]); - $collection->add("$route_name.$method", $route); - break; - - case 'PATCH': - // Restrict the incoming HTTP Content-type header to the known - // serialization formats. - $route->addRequirements(['_content_type_format' => implode('|', $this->serializerFormats)]); $collection->add("$route_name.$method", $route); break; diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php index 9f5137905b271956b87491d5c69417ee66d8f73a..e5437ccb680213f1f1ef04852c05a0a8f3ec8749 100644 --- a/core/modules/rest/src/RequestHandler.php +++ b/core/modules/rest/src/RequestHandler.php @@ -12,7 +12,6 @@ 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; @@ -89,43 +88,33 @@ public function handle(RouteMatchInterface $route_match, Request $request) { if (!empty($received)) { $format = $request->getContentType(); - // Only allow serialization formats that are explicitly configured. If no - // formats are configured allow all and hope that the serializer knows the - // format. If the serializer cannot handle it an exception will be thrown - // that bubbles up to the client. - $request_method = $request->getMethod(); - if (in_array($format, $resource_config->getFormats($request_method))) { - $definition = $resource->getPluginDefinition(); + $definition = $resource->getPluginDefinition(); - // First decode the request data. We can then determine if the - // serialized data was malformed. + // First decode the request data. We can then determine if the + // serialized data was malformed. + try { + $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->decode($received, $format, ['request_method' => $method]); + $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) { - // 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()); + throw new UnprocessableEntityHttpException($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()); - } + catch (InvalidArgumentException $e) { + throw new UnprocessableEntityHttpException($e->getMessage()); } } - else { - throw new UnsupportedMediaTypeHttpException(); - } } // Determine the request parameters that should be passed to the resource diff --git a/core/modules/rest/src/Routing/ResourceRoutes.php b/core/modules/rest/src/Routing/ResourceRoutes.php index 6aec267db2ce2f59c275aba92f9959f4f362588b..c21f13cd961c4d74d48188db7d6c324fcbfe0000 100644 --- a/core/modules/rest/src/Routing/ResourceRoutes.php +++ b/core/modules/rest/src/Routing/ResourceRoutes.php @@ -113,8 +113,15 @@ protected function getRoutesForResourceConfig(RestResourceConfigInterface $rest_ continue; } - // The configuration seems legit at this point, so we set the - // authentication provider and add the route. + // The configuration has been validated, so we update the route to: + // - set the allowed request body content types/formats for methods that + // allow request bodies to be sent + // - set the allowed authentication providers + if (in_array($method, ['POST', 'PATCH', 'PUT'], TRUE)) { + // Restrict the incoming HTTP Content-type header to the allowed + // formats. + $route->addRequirements(['_content_type_format' => implode('|', $rest_resource_config->getFormats($method))]); + } $route->setOption('_auth', $rest_resource_config->getAuthenticationProviders($method)); $route->setDefault('_rest_resource_config', $rest_resource_config->id()); $collection->add("rest.$name", $route); diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php index 53796c8aeaf490ad555c69bbb62fffe80866f92f..81baf9ee179d3630bc6a526e9e8a87990c0819aa 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php @@ -724,10 +724,7 @@ public function testPost() { // DX: 415 when request body in existing but not allowed format. $response = $this->request('POST', $url, $request_options); - // @todo Update this in https://www.drupal.org/node/2826407. Also move it - // higher, before the "no request body" test. That's impossible right now, - // because the format validation happens too late. - $this->assertResourceErrorResponse(415, '', $response); + $this->assertResourceErrorResponse(415, 'No route found that matches "Content-Type: text/xml"', $response); $request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType; @@ -936,10 +933,7 @@ public function testPatch() { // DX: 415 when request body in existing but not allowed format. $response = $this->request('PATCH', $url, $request_options); - // @todo Update this in https://www.drupal.org/node/2826407. Also move it - // higher, before the "no request body" test. That's impossible right now, - // because the format validation happens too late. - $this->assertResourceErrorResponse(415, '', $response); + $this->assertResourceErrorResponse(415, 'No route found that matches "Content-Type: text/xml"', $response); $request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;