comparison core/modules/jsonapi/src/EventSubscriber/JsonApiRequestValidator.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents
children
comparison
equal deleted inserted replaced
4:a9cd425dd02b 5:12f9dff5fda9
1 <?php
2
3 namespace Drupal\jsonapi\EventSubscriber;
4
5 use Drupal\Core\Cache\CacheableMetadata;
6 use Drupal\jsonapi\JsonApiSpec;
7 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
8 use Symfony\Component\HttpFoundation\Request;
9 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
10 use Drupal\Core\Http\Exception\CacheableBadRequestHttpException;
11 use Symfony\Component\HttpKernel\KernelEvents;
12
13 /**
14 * Request subscriber that validates a JSON:API request.
15 *
16 * @internal JSON:API maintains no PHP API. The API is the HTTP API. This class
17 * may change at any time and could break any dependencies on it.
18 *
19 * @see https://www.drupal.org/project/jsonapi/issues/3032787
20 * @see jsonapi.api.php
21 */
22 class JsonApiRequestValidator implements EventSubscriberInterface {
23
24 /**
25 * Validates JSON:API requests.
26 *
27 * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
28 * The event to process.
29 */
30 public function onRequest(GetResponseEvent $event) {
31 $request = $event->getRequest();
32 if ($request->getRequestFormat() !== 'api_json') {
33 return;
34 }
35
36 $this->validateQueryParams($request);
37 }
38
39 /**
40 * Validates custom (implementation-specific) query parameter names.
41 *
42 * @param \Symfony\Component\HttpFoundation\Request $request
43 * The request for which to validate JSON:API query parameters.
44 *
45 * @return \Drupal\jsonapi\ResourceResponse|null
46 * A JSON:API resource response.
47 *
48 * @see http://jsonapi.org/format/#query-parameters
49 */
50 protected function validateQueryParams(Request $request) {
51 $invalid_query_params = [];
52 foreach (array_keys($request->query->all()) as $query_parameter_name) {
53 // Ignore reserved (official) query parameters.
54 if (in_array($query_parameter_name, JsonApiSpec::getReservedQueryParameters())) {
55 continue;
56 }
57
58 if (!JsonApiSpec::isValidCustomQueryParameter($query_parameter_name)) {
59 $invalid_query_params[] = $query_parameter_name;
60 }
61 }
62
63 // Drupal uses the `_format` query parameter for Content-Type negotiation.
64 // Using it violates the JSON:API spec. Nudge people nicely in the correct
65 // direction. (This is special cased because using it is pretty common.)
66 if (in_array('_format', $invalid_query_params, TRUE)) {
67 $uri_without_query_string = $request->getSchemeAndHttpHost() . $request->getBaseUrl() . $request->getPathInfo();
68 $exception = new CacheableBadRequestHttpException((new CacheableMetadata())->addCacheContexts(['url.query_args:_format']), 'JSON:API does not need that ugly \'_format\' query string! 🤘 Use the URL provided in \'links\' 🙏');
69 $exception->setHeaders(['Link' => $uri_without_query_string]);
70 throw $exception;
71 }
72
73 if (empty($invalid_query_params)) {
74 return NULL;
75 }
76
77 $message = sprintf('The following query parameters violate the JSON:API spec: \'%s\'.', implode("', '", $invalid_query_params));
78 $exception = new CacheableBadRequestHttpException((new CacheableMetadata())->addCacheContexts(['url.query_args']), $message);
79 $exception->setHeaders(['Link' => 'http://jsonapi.org/format/#query-parameters']);
80 throw $exception;
81 }
82
83 /**
84 * {@inheritdoc}
85 */
86 public static function getSubscribedEvents() {
87 $events[KernelEvents::REQUEST][] = ['onRequest'];
88 return $events;
89 }
90
91 }