core/modules/rest/src/RequestHandler.php | 22 +--- .../serialization/serialization.services.yml | 5 + .../EventSubscriber/DefaultExceptionSubscriber.php | 124 +++++++++++++++++++++ 3 files changed, 130 insertions(+), 21 deletions(-) diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php index 0c0d05b..572a70c 100644 --- a/core/modules/rest/src/RequestHandler.php +++ b/core/modules/rest/src/RequestHandler.php @@ -87,27 +87,7 @@ public function handle(RouteMatchInterface $route_match, Request $request) { // parsing it out of the Accept headers again, we can simply retrieve the // format requirement. If there is no format associated, just pick JSON. $format = $route_match->getRouteObject()->getRequirement('_format') ?: 'json'; - try { - $response = call_user_func_array(array($resource, $method), array_merge($parameters, array($unserialized, $request))); - } - catch (HttpException $e) { - // In case a response is forbidden for the current user, we must not - // convert it into a response, because that would rob - // \Drupal\Core\EventSubscriber\AuthenticationSubscriber::onExceptionSendChallenge() - // from the opportunity to convert it into a 401 response, to challenge - // the user to authenticate. - // @see \Drupal\Core\Authentication\AuthenticationProviderChallengeInterface - if ($e->getStatusCode() == 403) { - throw $e; - } - - $error['error'] = $e->getMessage(); - $content = $serializer->serialize($error, $format); - // Add the default content type, but only if the headers from the - // exception have not specified it already. - $headers = $e->getHeaders() + array('Content-Type' => $request->getMimeType($format)); - return new Response($content, $e->getStatusCode(), $headers); - } + $response = call_user_func_array(array($resource, $method), array_merge($parameters, array($unserialized, $request))); return $response instanceof ResourceResponseInterface ? $this->renderResponse($request, $response, $serializer, $format) : diff --git a/core/modules/serialization/serialization.services.yml b/core/modules/serialization/serialization.services.yml index c510ab1..b110e45 100644 --- a/core/modules/serialization/serialization.services.yml +++ b/core/modules/serialization/serialization.services.yml @@ -64,3 +64,8 @@ services: class: Drupal\serialization\EntityResolver\TargetIdResolver tags: - { name: entity_resolver} + serialization.exception.default: + class: Drupal\serialization\EventSubscriber\DefaultExceptionSubscriber + tags: + - { name: event_subscriber } + arguments: ['@serializer', '%serializer.formats%'] diff --git a/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php new file mode 100644 index 0000000..5774a31 --- /dev/null +++ b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php @@ -0,0 +1,124 @@ +serializer = $serializer; + $this->serializerFormats = $serializer_formats; + } + + /** + * {@inheritdoc} + */ + protected function getHandledFormats() { + return $this->serializerFormats; + } + + /** + * {@inheritdoc} + */ + protected static function getPriority() { + // This will fire after the most common HTML handler, since HTML requests + // are still more common than HTTP requests. + return -75; + } + + /** + * Handles a 400 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on400(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_BAD_REQUEST); + } + + /** + * Handles a 403 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on403(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_FORBIDDEN); + } + + /** + * Handles a 404 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on404(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_NOT_FOUND); + } + + /** + * Handles a 405 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on405(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_METHOD_NOT_ALLOWED); + } + + /** + * Handles a 406 error for HTTP. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The event to process. + */ + public function on406(GetResponseForExceptionEvent $event) { + $this->setEventResponse($event, Response::HTTP_NOT_ACCEPTABLE); + } + + /** + * Sets the Response for the exception event. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event + * The current exception event. + * @param int $status + * The HTTP status code to set for the response. + */ + protected function setEventResponse(GetResponseForExceptionEvent $event, $status) { + $format = $event->getRequest()->getRequestFormat(); + $content = ['message' => $event->getException()->getMessage()]; + $encoded_content = $this->serializer->serialize($content, $format); + $response = new Response($encoded_content, $status); + $event->setResponse($response); + } + +}