From 0050a4dafb18a5a616cc33fe5fc8027b03045158 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Tue, 11 Feb 2020 09:34:35 -0500 Subject: [PATCH] [HttpFoundation] Do not set the default Content-Type based on the Accept header --- .../ErrorRenderer/SerializerErrorRenderer.php | 9 ++++++++- src/Symfony/Component/HttpFoundation/Request.php | 4 +++- src/Symfony/Component/HttpFoundation/Response.php | 2 +- .../HttpFoundation/Tests/ResponseTest.php | 15 ++++++++++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php index d1dd652a64..e0640850a6 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php +++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php @@ -12,6 +12,7 @@ namespace Symfony\Component\ErrorHandler\ErrorRenderer; use Symfony\Component\ErrorHandler\Exception\FlattenException; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Serializer\Exception\NotEncodableValueException; use Symfony\Component\Serializer\SerializerInterface; @@ -30,6 +31,7 @@ class SerializerErrorRenderer implements ErrorRendererInterface /** * @param string|callable(FlattenException) $format The format as a string or a callable that should return it + * formats not supported by Request::getMimeTypes() should be given as mime types * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it */ public function __construct(SerializerInterface $serializer, $format, ErrorRendererInterface $fallbackErrorRenderer = null, $debug = false) @@ -57,11 +59,16 @@ class SerializerErrorRenderer implements ErrorRendererInterface try { $format = \is_string($this->format) ? $this->format : ($this->format)($flattenException); + $headers = [ + 'Content-Type' => Request::getMimeTypes($format)[0] ?? $format, + 'Vary' => 'Accept', + ]; return $flattenException->setAsString($this->serializer->serialize($flattenException, $format, [ 'exception' => $exception, 'debug' => \is_bool($this->debug) ? $this->debug : ($this->debug)($exception), - ])); + ])) + ->setHeaders($flattenException->getHeaders() + $headers); } catch (NotEncodableValueException $e) { return $this->fallbackErrorRenderer->render($exception); } diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index a5e50eb795..33f4efb5ed 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1590,7 +1590,9 @@ class Request * Gets the preferred format for the response by inspecting, in the following order: * * the request format set using setRequestFormat * * the values of the Accept HTTP header - * * the content type of the body of the request. + * + * Note that if you use this method, you should send the "Vary: Accept" header + * in the response to prevent any issues with intermediary HTTP caches. */ public function getPreferredFormat(?string $default = 'html'): ?string { diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 714109d536..b9177934a4 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -275,7 +275,7 @@ class Response } else { // Content-type based on the Request if (!$headers->has('Content-Type')) { - $format = $request->getPreferredFormat(null); + $format = $request->getRequestFormat(null); if (null !== $format && $mimeType = $request->getMimeType($format)) { $headers->set('Content-Type', $mimeType); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index d5da59ad7e..c766f88a29 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -497,12 +497,25 @@ class ResponseTest extends ResponseTestCase $this->assertEquals('text/html; charset=UTF-8', $response->headers->get('content-type')); } + /** + * Same URL cannot produce different Content-Type based on the value of the Accept header, + * unless explicitly stated in the response object. + */ + public function testPrepareDoesNotSetContentTypeBasedOnRequestAcceptHeader() + { + $response = new Response('foo'); + $request = Request::create('/'); + $request->headers->set('Accept', 'application/json'); + $response->prepare($request); + + $this->assertSame('text/html; charset=UTF-8', $response->headers->get('content-type')); + } + public function testPrepareSetContentType() { $response = new Response('foo'); $request = Request::create('/'); $request->setRequestFormat('json'); - $request->headers->remove('accept'); $response->prepare($request);