annotate core/modules/jsonapi/src/EventSubscriber/JsonApiRequestValidator.php @ 19:fa3358dc1485 tip

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