Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 61 |
RequestHandler | |
0.00% |
0 / 1 |
|
0.00% |
0 / 3 |
182 | |
0.00% |
0 / 61 |
handle | |
0.00% |
0 / 1 |
132 | |
0.00% |
0 / 48 |
|||
anonymous function | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
csrfToken | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\rest\RequestHandler. | |
*/ | |
namespace Drupal\rest; | |
use Drupal\Core\Render\RenderContext; | |
use Drupal\Core\Routing\RouteMatchInterface; | |
use Symfony\Component\DependencyInjection\ContainerAwareInterface; | |
use Symfony\Component\DependencyInjection\ContainerAwareTrait; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\HttpKernel\Exception\HttpException; | |
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; | |
use Symfony\Component\Serializer\Exception\UnexpectedValueException; | |
/** | |
* Acts as intermediate request forwarder for resource plugins. | |
*/ | |
class RequestHandler implements ContainerAwareInterface { | |
use ContainerAwareTrait; | |
/** | |
* Handles a web API request. | |
* | |
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match | |
* The route match. | |
* @param \Symfony\Component\HttpFoundation\Request $request | |
* The HTTP request object. | |
* | |
* @return \Symfony\Component\HttpFoundation\Response | |
* The response object. | |
*/ | |
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); | |
} | |
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; | |
} | |
/** | |
* Generates a CSRF protecting session token. | |
* | |
* @return \Symfony\Component\HttpFoundation\Response | |
* The response object. | |
*/ | |
public function csrfToken() { | |
return new Response(\Drupal::csrfToken()->get('rest'), 200, array('Content-Type' => 'text/plain')); | |
} | |
} |