Last active
November 14, 2024 14:01
-
-
Save soyuka/beac00bc527d2ea6e703ed27dd67532c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* This file is part of the API Platform project. | |
* | |
* (c) Kévin Dunglas <[email protected]> | |
* | |
* For the full copyright and license information, please view the LICENSE | |
* file that was distributed with this source code. | |
*/ | |
declare(strict_types=1); | |
namespace ApiPlatform\State\Provider; | |
use ApiPlatform\Metadata\Error as ErrorOperation; | |
use ApiPlatform\Metadata\HttpOperation; | |
use ApiPlatform\Metadata\Operation; | |
use ApiPlatform\Metadata\Util\ContentNegotiationTrait; | |
use ApiPlatform\State\ProviderInterface; | |
use Negotiation\Negotiator; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; | |
final class ContentNegotiationProvider implements ProviderInterface | |
{ | |
use ContentNegotiationTrait; | |
/** | |
* @param array<string, string[]> $formats | |
* @param array<string, string[]> $errorFormats | |
*/ | |
public function __construct(private readonly ?ProviderInterface $decorated = null, ?Negotiator $negotiator = null, private readonly array $formats = [], private readonly array $errorFormats = []) | |
{ | |
$this->negotiator = $negotiator ?? new Negotiator(); | |
} | |
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null | |
{ | |
if (!($request = $context['request'] ?? null) || !$operation instanceof HttpOperation) { | |
return $this->decorated?->provide($operation, $uriVariables, $context); | |
} | |
$isErrorOperation = $operation instanceof ErrorOperation; | |
$formats = $operation->getOutputFormats() ?? ($isErrorOperation ? $this->errorFormats : $this->formats); | |
$this->addRequestFormats($request, $formats); | |
$request->attributes->set('input_format', $this->getInputFormat($operation, $request)); | |
if (!$isErrorOperation) { | |
$request->setRequestFormat($this->getRequestFormat($request, $formats)); | |
} else { | |
$request->setRequestFormat($this->getRequestFormat($request, $formats, false)); | |
} | |
return $this->decorated?->provide($operation, $uriVariables, $context); | |
} | |
/** | |
* Adds the supported formats to the request. | |
* | |
* This is necessary for {@see Request::getMimeType} and {@see Request::getMimeTypes} to work. | |
* Note that this replaces default mime types configured at {@see Request::initializeFormats} | |
* | |
* @param array<string, string|string[]> $formats | |
*/ | |
private function addRequestFormats(Request $request, array $formats): void | |
{ | |
foreach ($formats as $format => $mimeTypes) { | |
$request->setFormat($format, (array) $mimeTypes); | |
} | |
} | |
/** | |
* Flattened the list of MIME types. | |
* | |
* @param array<string, string|string[]> $formats | |
* | |
* @return array<string, string> | |
*/ | |
private function flattenMimeTypes(array $formats): array | |
{ | |
$flattenedMimeTypes = []; | |
foreach ($formats as $format => $mimeTypes) { | |
foreach ($mimeTypes as $mimeType) { | |
$flattenedMimeTypes[$mimeType] = $format; | |
} | |
} | |
return $flattenedMimeTypes; | |
} | |
/** | |
* Extracts the format from the Content-Type header and check that it is supported. | |
* | |
* @throws UnsupportedMediaTypeHttpException | |
*/ | |
private function getInputFormat(HttpOperation $operation, Request $request): ?string | |
{ | |
$contentType = $request->headers->get('CONTENT_TYPE'); | |
if (null === $contentType || '' === $contentType) { | |
return null; | |
} | |
/** @var string $contentType */ | |
$formats = $operation->getInputFormats() ?? []; | |
if ($format = $this->getMimeTypeFormat($contentType, $formats)) { | |
return $format; | |
} | |
if (!$request->isMethodSafe() && 'DELETE' !== $request->getMethod()) { | |
throw new UnsupportedMediaTypeHttpException(\sprintf('The content-type "%s" is not supported. Supported MIME types are "%s".', $contentType, implode('", "', array_keys($this->flattenMimeTypes($formats))))); | |
} | |
return null; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment