Newer
Older
Dries Buytaert
committed
<?php
/**
* @file
* Definition of Drupal\rest\RequestHandler.
*/
namespace Drupal\rest;
Alex Pott
committed
use Drupal\Core\Routing\RouteMatchInterface;
Alex Pott
committed
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
Dries Buytaert
committed
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
Dries Buytaert
committed
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
Dries Buytaert
committed
/**
* Acts as intermediate request forwarder for resource plugins.
*/
Alex Pott
committed
class RequestHandler implements ContainerAwareInterface {
use ContainerAwareTrait;
Dries Buytaert
committed
/**
* Handles a web API request.
*
Alex Pott
committed
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param \Symfony\Component\HttpFoundation\Request $request
Dries Buytaert
committed
* The HTTP request object.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
Dries Buytaert
committed
*
Angie Byron
committed
* @return \Symfony\Component\HttpFoundation\Response
* The response object.
Dries Buytaert
committed
*/
public function handle(RouteMatchInterface $route_match, Request $request, RouteMatchInterface $route_match) {
catch
committed
Alex Pott
committed
$plugin = $route_match->getRouteObject()->getDefault('_plugin');
Dries Buytaert
committed
$method = strtolower($request->getMethod());
$resource = $this->container
->get('plugin.manager.rest')
->getInstance(array('id' => $plugin));
Alex Pott
committed
// 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'])) {
Alex Pott
committed
$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)));
}
Dries Buytaert
committed
}
else {
throw new UnsupportedMediaTypeHttpException();
Dries Buytaert
committed
}
}
catch
committed
// Determine the request parameters that should be passed to the resource
// plugin.
$route_parameters = $route_match->getParameters();
catch
committed
$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
Angie Byron
committed
// format requirement. If there is no format associated, just pick JSON.
Alex Pott
committed
$format = $route_match->getRouteObject()->getRequirement('_format') ?: 'json';
catch
committed
$response = call_user_func_array(array($resource, $method), array_merge($parameters, array($unserialized, $request)));
Dries Buytaert
committed
}
catch (HttpException $e) {
Dries Buytaert
committed
$error['error'] = $e->getMessage();
$content = $serializer->serialize($error, $format);
Dries Buytaert
committed
// 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));
Dries Buytaert
committed
return new Response($content, $e->getStatusCode(), $headers);
// Serialize the outgoing data for the response, if available.
$data = $response->getResponseData();
if ($data != NULL) {
$output = $serializer->serialize($data, $format);
$response->setContent($output);
$response->headers->set('Content-Type', $request->getMimeType($format));
}
return $response;
Dries Buytaert
committed
}
/**
* 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'));
}