diff --git a/src/State/Provider/ContentNegotiationProvider.php b/src/State/Provider/ContentNegotiationProvider.php index 0ffd42607b6..d9f242ce85e 100644 --- a/src/State/Provider/ContentNegotiationProvider.php +++ b/src/State/Provider/ContentNegotiationProvider.php @@ -97,6 +97,14 @@ private function flattenMimeTypes(array $formats): array */ private function getInputFormat(HttpOperation $operation, Request $request): ?string { + if ( + false === ($input = $operation->getInput()) + || (\is_array($input) && null === $input['class']) + || false === $operation->canDeserialize() + ) { + return null; + } + $contentType = $request->headers->get('CONTENT_TYPE'); if (null === $contentType || '' === $contentType) { return null; @@ -108,14 +116,14 @@ private function getInputFormat(HttpOperation $operation, Request $request): ?st return $format; } - $supportedMimeTypes = []; - foreach ($formats as $mimeTypes) { - foreach ($mimeTypes as $mimeType) { - $supportedMimeTypes[] = $mimeType; + if (!$request->isMethodSafe() && 'DELETE' !== $request->getMethod()) { + $supportedMimeTypes = []; + foreach ($formats as $mimeTypes) { + foreach ($mimeTypes as $mimeType) { + $supportedMimeTypes[] = $mimeType; + } } - } - if (!$request->isMethodSafe() && 'DELETE' !== $request->getMethod()) { throw new UnsupportedMediaTypeHttpException(\sprintf('The content-type "%s" is not supported. Supported MIME types are "%s".', $contentType, implode('", "', $supportedMimeTypes))); } diff --git a/tests/State/Provider/ContentNegotiationProviderTest.php b/tests/State/Provider/ContentNegotiationProviderTest.php index e4eb99ba904..dd610bd4b8d 100644 --- a/tests/State/Provider/ContentNegotiationProviderTest.php +++ b/tests/State/Provider/ContentNegotiationProviderTest.php @@ -21,6 +21,7 @@ use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; class ContentNegotiationProviderTest extends TestCase { @@ -60,4 +61,63 @@ public function testRequestWithEmptyContentType(): void $this->assertSame($expectedResult, $result); } + + public function testRequestWhenNoInput(): void + { + $expectedResult = new \stdClass(); + + $decorated = $this->prophesize(ProviderInterface::class); + $decorated->provide(Argument::cetera())->willReturn($expectedResult); + + $negotiator = new Negotiator(); + $formats = ['jsonld' => ['application/ld+json']]; + $errorFormats = ['jsonld' => ['application/ld+json']]; + + $provider = new ContentNegotiationProvider($decorated->reveal(), $negotiator, $formats, $errorFormats); + + $request = new Request( + server: [ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/', + 'CONTENT_TYPE' => 'some-not-supported/content-type', + ], + content: '' + ); + + $operation = new Post(); + $operation = $operation->withDeserialize(false); + $context = ['request' => $request]; + + $result = $provider->provide($operation, [], $context); + + $this->assertSame($expectedResult, $result); + } + + public function testRequestWithInput(): void + { + $this->expectException(UnsupportedMediaTypeHttpException::class); + + $decorated = $this->prophesize(ProviderInterface::class); + + $negotiator = new Negotiator(); + $formats = ['jsonld' => ['application/ld+json']]; + $errorFormats = ['jsonld' => ['application/ld+json']]; + + $provider = new ContentNegotiationProvider($decorated->reveal(), $negotiator, $formats, $errorFormats); + + $request = new Request( + server: [ + 'REQUEST_METHOD' => 'POST', + 'REQUEST_URI' => '/', + 'CONTENT_TYPE' => 'some-not-supported/content-type', + ], + content: '' + ); + + $operation = new Post(); + $operation = $operation->withDeserialize(); + $context = ['request' => $request]; + + $provider->provide($operation, [], $context); + } }