Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / rest / src / RequestHandler.php
1 <?php
2
3 namespace Drupal\rest;
4
5 use Drupal\Core\Cache\CacheableResponseInterface;
6 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
7 use Drupal\Core\Entity\EntityStorageInterface;
8 use Drupal\Core\Routing\RouteMatchInterface;
9 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
10 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
11 use Symfony\Component\DependencyInjection\ContainerInterface;
12 use Symfony\Component\HttpFoundation\Request;
13 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
14 use Symfony\Component\Serializer\Exception\UnexpectedValueException;
15
16 /**
17  * Acts as intermediate request forwarder for resource plugins.
18  *
19  * @see \Drupal\rest\EventSubscriber\ResourceResponseSubscriber
20  */
21 class RequestHandler implements ContainerAwareInterface, ContainerInjectionInterface {
22
23   use ContainerAwareTrait;
24
25   /**
26    * The resource configuration storage.
27    *
28    * @var \Drupal\Core\Entity\EntityStorageInterface
29    */
30   protected $resourceStorage;
31
32   /**
33    * Creates a new RequestHandler instance.
34    *
35    * @param \Drupal\Core\Entity\EntityStorageInterface $entity_storage
36    *   The resource configuration storage.
37    */
38   public function __construct(EntityStorageInterface $entity_storage) {
39     $this->resourceStorage = $entity_storage;
40   }
41
42   /**
43    * {@inheritdoc}
44    */
45   public static function create(ContainerInterface $container) {
46     return new static($container->get('entity_type.manager')->getStorage('rest_resource_config'));
47   }
48
49   /**
50    * Handles a web API request.
51    *
52    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
53    *   The route match.
54    * @param \Symfony\Component\HttpFoundation\Request $request
55    *   The HTTP request object.
56    *
57    * @return \Symfony\Component\HttpFoundation\Response
58    *   The response object.
59    */
60   public function handle(RouteMatchInterface $route_match, Request $request) {
61     $method = strtolower($request->getMethod());
62
63     // Symfony is built to transparently map HEAD requests to a GET request. In
64     // the case of the REST module's RequestHandler though, we essentially have
65     // our own light-weight routing system on top of the Drupal/symfony routing
66     // system. So, we have to do the same as what the UrlMatcher does: map HEAD
67     // requests to the logic for GET. This also guarantees response headers for
68     // HEAD requests are identical to those for GET requests, because we just
69     // return a GET response. Response::prepare() will transform it to a HEAD
70     // response at the very last moment.
71     // @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4
72     // @see \Symfony\Component\Routing\Matcher\UrlMatcher::matchCollection()
73     // @see \Symfony\Component\HttpFoundation\Response::prepare()
74     if ($method === 'head') {
75       $method = 'get';
76     }
77
78     $resource_config_id = $route_match->getRouteObject()->getDefault('_rest_resource_config');
79     /** @var \Drupal\rest\RestResourceConfigInterface $resource_config */
80     $resource_config = $this->resourceStorage->load($resource_config_id);
81     $resource = $resource_config->getResourcePlugin();
82
83     // Deserialize incoming data if available.
84     /** @var \Symfony\Component\Serializer\SerializerInterface $serializer */
85     $serializer = $this->container->get('serializer');
86     $received = $request->getContent();
87     $unserialized = NULL;
88     if (!empty($received)) {
89       $format = $request->getContentType();
90
91       $definition = $resource->getPluginDefinition();
92       try {
93         if (!empty($definition['serialization_class'])) {
94           $unserialized = $serializer->deserialize($received, $definition['serialization_class'], $format, ['request_method' => $method]);
95         }
96         // If the plugin does not specify a serialization class just decode
97         // the received data.
98         else {
99           $unserialized = $serializer->decode($received, $format, ['request_method' => $method]);
100         }
101       }
102       catch (UnexpectedValueException $e) {
103         throw new BadRequestHttpException($e->getMessage());
104       }
105     }
106
107     // Determine the request parameters that should be passed to the resource
108     // plugin.
109     $route_parameters = $route_match->getParameters();
110     $parameters = [];
111     // Filter out all internal parameters starting with "_".
112     foreach ($route_parameters as $key => $parameter) {
113       if ($key{0} !== '_') {
114         $parameters[] = $parameter;
115       }
116     }
117
118     // Invoke the operation on the resource plugin.
119     $response = call_user_func_array([$resource, $method], array_merge($parameters, [$unserialized, $request]));
120
121     if ($response instanceof CacheableResponseInterface) {
122       // Add rest config's cache tags.
123       $response->addCacheableDependency($resource_config);
124     }
125
126     return $response;
127   }
128
129 }