You are here

public function RequestHandler::handle in Zircon Profile 8

Same name and namespace in other branches
  1. 8.0 core/modules/rest/src/RequestHandler.php \Drupal\rest\RequestHandler::handle()

Handles a web API request.

Parameters

\Drupal\Core\Routing\RouteMatchInterface $route_match: The route match.

\Symfony\Component\HttpFoundation\Request $request: The HTTP request object.

Return value

\Symfony\Component\HttpFoundation\Response The response object.

File

core/modules/rest/src/RequestHandler.php, line 38
Contains \Drupal\rest\RequestHandler.

Class

RequestHandler
Acts as intermediate request forwarder for resource plugins.

Namespace

Drupal\rest

Code

public function handle(RouteMatchInterface $route_match, Request $request) {
  $plugin = $route_match
    ->getRouteObject()
    ->getDefault('_plugin');
  $method = strtolower($request
    ->getMethod());
  $resource = $this->container
    ->get('plugin.manager.rest')
    ->getInstance(array(
    'id' => $plugin,
  ));

  // Deserialize incoming data if available.
  $serializer = $this->container
    ->get('serializer');
  $received = $request
    ->getContent();
  $unserialized = NULL;
  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.
    $config = $this->container
      ->get('config.factory')
      ->get('rest.settings')
      ->get('resources');
    $method_settings = $config[$plugin][$request
      ->getMethod()];
    if (empty($method_settings['supported_formats']) || in_array($format, $method_settings['supported_formats'])) {
      $definition = $resource
        ->getPluginDefinition();
      $class = $definition['serialization_class'];
      try {
        $unserialized = $serializer
          ->deserialize($received, $class, $format, array(
          'request_method' => $method,
        ));
      } catch (UnexpectedValueException $e) {
        $error['error'] = $e
          ->getMessage();
        $content = $serializer
          ->serialize($error, $format);
        return new Response($content, 400, array(
          'Content-Type' => $request
            ->getMimeType($format),
        ));
      }
    }
    else {
      throw new UnsupportedMediaTypeHttpException();
    }
  }

  // Determine the request parameters that should be passed to the resource
  // plugin.
  $route_parameters = $route_match
    ->getParameters();
  $parameters = array();

  // Filter out all internal parameters starting with "_".
  foreach ($route_parameters as $key => $parameter) {
    if ($key[0] !== '_') {
      $parameters[] = $parameter;
    }
  }

  // Invoke the operation on the resource plugin.
  // All REST routes are restricted to exactly one format, so instead of
  // 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) {
    $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);
  }

  // Serialize the outgoing data for the response, if available.
  if ($response instanceof ResourceResponse && ($data = $response
    ->getResponseData())) {

    // Serialization can invoke rendering (e.g., generating URLs), but the
    // serialization API does not provide a mechanism to collect the
    // bubbleable metadata associated with that (e.g., language and other
    // contexts), so instead, allow those to "leak" and collect them here in
    // a render context.
    // @todo Add test coverage for language negotiation contexts in
    //   https://www.drupal.org/node/2135829.
    $context = new RenderContext();
    $output = $this->container
      ->get('renderer')
      ->executeInRenderContext($context, function () use ($serializer, $data, $format) {
      return $serializer
        ->serialize($data, $format);
    });
    $response
      ->setContent($output);
    if (!$context
      ->isEmpty()) {
      $response
        ->addCacheableDependency($context
        ->pop());
    }
    $response->headers
      ->set('Content-Type', $request
      ->getMimeType($format));

    // Add rest settings config's cache tags.
    $response
      ->addCacheableDependency($this->container
      ->get('config.factory')
      ->get('rest.settings'));
  }
  return $response;
}