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