From ecc1ffd024541e0fb31da6510b114eb78a4fe579 Mon Sep 17 00:00:00 2001 From: Helmut Hummel <typo3@helhum.io> Date: Wed, 14 Dec 2022 18:55:50 +0100 Subject: [PATCH] [BUGFIX] Send correct content type for cached Extbase actions The setContentType method on TyposcripFrontendController was introduced to allow Extbase plugins to change the content-type header of the server http response. However this currently only works, when the Extbase plugin action is uncached. Once a plugin, that is rendered on a page ist cached in page cache, the content type of subsequent requests is always "text/html", because the value of the contentType property is not stored in cache. Storing this value into cache allows creating e.g. Json APIs as Extbase Plugins, that can be fully cached without adding hacks like modifying config.additionalHeaders Resolves: #99373 Releases: main, 11.5 Change-Id: Ibf00c9438d8763ef9d32c6ad7d00a44d3137ba13 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/77269 Tested-by: core-ci <typo3@b13.com> Reviewed-by: Helmut Hummel <typo3@helhum.io> Tested-by: Helmut Hummel <typo3@helhum.io> --- .../Classes/Controller/QueueController.php | 5 +---- .../Classes/Controller/QueueController.php | 5 +---- .../sysext/extbase/Classes/Core/Bootstrap.php | 20 +++++-------------- .../TypoScriptFrontendController.php | 5 ++++- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Classes/Controller/QueueController.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Classes/Controller/QueueController.php index 6dd96e729b76..991647c89c72 100644 --- a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Classes/Controller/QueueController.php +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Classes/Controller/QueueController.php @@ -90,13 +90,10 @@ class QueueController extends ActionController public function finishAction(): ResponseInterface { - $typoScriptFrontendController = $GLOBALS['TSFE']; - $typoScriptFrontendController->setContentType('application/json'); - $value = $this->queueService->getValues(); $this->view->assign('value', $value); $body = new Stream('php://temp', 'rw'); $body->write($this->view->render()); - return new Response($body); + return (new Response($body))->withHeader('Content-Type', 'application/json; charset=utf-8'); } } diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_irre_foreignfield/Classes/Controller/QueueController.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_irre_foreignfield/Classes/Controller/QueueController.php index c221eb3654ae..8b518e6c6f14 100644 --- a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_irre_foreignfield/Classes/Controller/QueueController.php +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_irre_foreignfield/Classes/Controller/QueueController.php @@ -90,13 +90,10 @@ class QueueController extends ActionController public function finishAction(): ResponseInterface { - $typoScriptFrontendController = $GLOBALS['TSFE']; - $typoScriptFrontendController->setContentType('application/json'); - $value = $this->queueService->getValues(); $this->view->assign('value', $value); $body = new Stream('php://temp', 'rw'); $body->write($this->view->render()); - return new Response($body); + return (new Response($body))->withHeader('Content-Type', 'application/json; charset=utf-8'); } } diff --git a/typo3/sysext/extbase/Classes/Core/Bootstrap.php b/typo3/sysext/extbase/Classes/Core/Bootstrap.php index a65100315c8a..2b9b79b8bcbe 100644 --- a/typo3/sysext/extbase/Classes/Core/Bootstrap.php +++ b/typo3/sysext/extbase/Classes/Core/Bootstrap.php @@ -176,24 +176,14 @@ class Bootstrap $this->clearCacheOnError(); } - // In case TSFE is available and this is a json response, we have - // to take the TypoScript settings regarding charset into account. - // @todo Since HTML5 only utf-8 is a valid charset, this settings should be deprecated + // In case TSFE is available and this is a json response, we have to let TSFE know we have a specific Content-Type if (($typoScriptFrontendController = ($GLOBALS['TSFE'] ?? null)) instanceof TypoScriptFrontendController - && strpos($response->getHeaderLine('Content-Type'), 'application/json') === 0 + && $response->hasHeader('Content-Type') ) { - // Unset the already defined Content-Type + [$contentType] = explode(';', $response->getHeaderLine('Content-Type')); + // Do not send the header directly (see below) $response = $response->withoutHeader('Content-Type'); - if (empty($typoScriptFrontendController->config['config']['disableCharsetHeader'])) { - // If the charset header is *not* disabled in configuration, - // TypoScriptFrontendController will send the header later with the Content-Type which we set here. - $typoScriptFrontendController->setContentType('application/json'); - } else { - // Although the charset header is disabled in configuration, we *must* send a Content-Type header here. - // Content-Type headers optionally carry charset information at the same time. - // Since we have the information about the charset, there is no reason to not include the charset information although disabled in TypoScript. - $response = $response->withHeader('Content-Type', 'application/json; charset=' . trim($typoScriptFrontendController->metaCharset)); - } + $typoScriptFrontendController->setContentType($contentType); } if (headers_sent() === false) { diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php index b2672237a7aa..3e46d116c810 100644 --- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php +++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php @@ -663,7 +663,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface /** * @param string $contentType - * @internal Should only be used by TYPO3 core for now + * @internal Must only be used by TYPO3 core */ public function setContentType($contentType) { @@ -1676,6 +1676,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface $this->config = $cachedData['cache_data']; // Getting the content $this->content = $cachedData['content']; + // Getting the content type + $this->contentType = $cachedData['contentType'] ?? $this->contentType; // Setting flag, so we know, that some cached content has been loaded $this->cacheContentFlag = true; $this->cacheExpires = $cachedData['expires']; @@ -2374,6 +2376,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface 'identifier' => $this->newHash, 'page_id' => $this->id, 'content' => $content, + 'contentType' => $this->contentType, 'cache_data' => $data, 'expires' => $expirationTstamp, 'tstamp' => $GLOBALS['EXEC_TIME'], -- GitLab