From d7e560a12c0aa262d1720bbf10e9a69620728fa9 Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Sat, 9 Dec 2017 00:10:36 +0100
Subject: [PATCH] [TASK] Use JsonResponse for all Backend AJAX calls

The new JsonResponse object should be used throughout the
TYPO3 Backend in order to properly call json_encode() with
the right flags automatically, making the code inside
the controllers simpler.

Resolves: #83267
Releases: master
Change-Id: Ie03fb1414a965a61632897f0e25645a67f3225a5
Reviewed-on: https://review.typo3.org/54995
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Stefan Neufeind <typo3.neufeind@speedpartner.de>
Tested-by: Stefan Neufeind <typo3.neufeind@speedpartner.de>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
---
 .../ToolbarItems/ShortcutToolbarItem.php      |  7 ++--
 .../Controller/AjaxLoginController.php        | 33 ++++++---------
 .../Classes/Controller/BackendController.php  | 15 +++----
 .../Controller/ContextHelpAjaxController.php  |  7 ++--
 .../Controller/ContextMenuController.php      | 13 +++---
 .../Controller/File/FileController.php        |  9 ++--
 .../FileSystemNavigationFrameController.php   | 10 ++---
 .../Controller/FlashMessageController.php     |  7 ++--
 .../Controller/FormFlexAjaxController.php     |  8 ++--
 .../Controller/FormInlineAjaxController.php   | 41 +++++++------------
 .../FormSelectTreeAjaxController.php          |  8 ++--
 .../Controller/LinkBrowserController.php      |  8 ++--
 .../Controller/LiveSearchController.php       |  7 ++--
 .../Controller/OnlineMediaController.php      |  6 +--
 .../Page/LocalizationController.php           | 10 ++---
 .../SimpleDataHandlerController.php           |  8 ++--
 .../Controller/UserSettingsController.php     |  8 ++--
 .../Wizard/SuggestWizardController.php        |  8 ++--
 .../Classes/Controller/DocumentController.php |  7 ++--
 .../Controller/RecyclerAjaxController.php     |  7 ++--
 .../t3editor/Classes/CodeCompletion.php       |  9 ++--
 .../Classes/TypoScriptReferenceLoader.php     |  7 ++--
 .../Classes/Controller/AjaxController.php     |  8 ++--
 .../Classes/Controller/AjaxDispatcher.php     |  7 ++--
 24 files changed, 103 insertions(+), 155 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Backend/ToolbarItems/ShortcutToolbarItem.php b/typo3/sysext/backend/Classes/Backend/ToolbarItems/ShortcutToolbarItem.php
index 456346ecaece..ff8d5b26f29b 100644
--- a/typo3/sysext/backend/Classes/Backend/ToolbarItems/ShortcutToolbarItem.php
+++ b/typo3/sysext/backend/Classes/Backend/ToolbarItems/ShortcutToolbarItem.php
@@ -23,6 +23,7 @@ use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\QueryHelper;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Page\PageRenderer;
@@ -481,10 +482,9 @@ class ShortcutToolbarItem implements ToolbarItemInterface
      * Deletes a shortcut through an AJAX call
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function removeShortcutAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function removeShortcutAction(ServerRequestInterface $request): ResponseInterface
     {
         $parsedBody = $request->getParsedBody();
         $queryParams = $request->getQueryParams();
@@ -507,8 +507,7 @@ class ShortcutToolbarItem implements ToolbarItemInterface
                 $success = true;
             }
         }
-        $response->getBody()->write(json_encode(['success' => $success]));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, ['success' => $success]);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/AjaxLoginController.php b/typo3/sysext/backend/Classes/Controller/AjaxLoginController.php
index b2cef02d8ceb..2866fe7ef61b 100644
--- a/typo3/sysext/backend/Classes/Controller/AjaxLoginController.php
+++ b/typo3/sysext/backend/Classes/Controller/AjaxLoginController.php
@@ -17,6 +17,8 @@ namespace TYPO3\CMS\Backend\Controller;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Http\JsonResponse;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * This is the ajax handler for backend login after timeout.
@@ -31,10 +33,9 @@ class AjaxLoginController
      * If it was unsuccessful, we display that and show the login box again.
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function loginAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function loginAction(ServerRequestInterface $request): ResponseInterface
     {
         if ($this->isAuthorizedBackendSession()) {
             $result = ['success' => true];
@@ -46,58 +47,49 @@ class AjaxLoginController
         } else {
             $result = ['success' => false];
         }
-
-        $response->getBody()->write(json_encode(['login' => $result]));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, ['login' => $result]);
     }
 
     /**
      * Logs out the current BE user
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function logoutAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function logoutAction(ServerRequestInterface $request): ResponseInterface
     {
         $backendUser = $this->getBackendUser();
         $backendUser->logoff();
-
-        $response->getBody()->write(json_encode([
+        return GeneralUtility::makeInstance(JsonResponse::class, [
             'logout' => [
                 'success' => !isset($backendUser->user['uid'])
             ]
-        ]));
-        return $response;
+        ]);
     }
 
     /**
      * Refreshes the login without needing login information. We just refresh the session.
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function refreshAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function refreshAction(ServerRequestInterface $request): ResponseInterface
     {
         $this->getBackendUser()->checkAuthentication();
-
-        $response->getBody()->write(json_encode([
+        return GeneralUtility::makeInstance(JsonResponse::class, [
             'refresh' => [
                 'success' => true
             ]
-        ]));
-        return $response;
+        ]);
     }
 
     /**
      * Checks if the user session is expired yet
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function isTimedOutAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function isTimedOutAction(ServerRequestInterface $request): ResponseInterface
     {
         $session = [
             'timed_out' => false,
@@ -117,8 +109,7 @@ class AjaxLoginController
             // 120 is somewhat arbitrary to allow for a little room during the countdown and load times, etc.
             $session['will_time_out'] = $GLOBALS['EXEC_TIME'] >= $ses_tstamp + $timeout - 120;
         }
-        $response->getBody()->write(json_encode(['login' => $session]));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, ['login' => $session]);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/BackendController.php b/typo3/sysext/backend/Classes/Controller/BackendController.php
index bd78f9d0f13b..7078dc39a84f 100644
--- a/typo3/sysext/backend/Classes/Controller/BackendController.php
+++ b/typo3/sysext/backend/Classes/Controller/BackendController.php
@@ -25,6 +25,7 @@ use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
@@ -585,28 +586,22 @@ class BackendController
      * Returns the Module menu for the AJAX request
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function getModuleMenu(ServerRequestInterface $request, ResponseInterface $response)
+    public function getModuleMenu(ServerRequestInterface $request): ResponseInterface
     {
-        $content = $this->generateModuleMenu();
-
-        $response->getBody()->write(json_encode(['menu' => $content]));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, ['menu' => $this->generateModuleMenu()]);
     }
 
     /**
      * Returns the toolbar for the AJAX request
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function getTopbar(ServerRequestInterface $request, ResponseInterface $response)
+    public function getTopbar(ServerRequestInterface $request): ResponseInterface
     {
-        $response->getBody()->write(json_encode(['topbar' => $this->renderTopbar()]));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, ['topbar' => $this->renderTopbar()]);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/ContextHelpAjaxController.php b/typo3/sysext/backend/Classes/Controller/ContextHelpAjaxController.php
index 8399e0dfae0b..91c56a01615a 100644
--- a/typo3/sysext/backend/Classes/Controller/ContextHelpAjaxController.php
+++ b/typo3/sysext/backend/Classes/Controller/ContextHelpAjaxController.php
@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Backend\Controller;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -33,16 +34,16 @@ class ContextHelpAjaxController
      * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function getHelpAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function getHelpAction(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
     {
         $params = isset($request->getParsedBody()['params']) ? $request->getParsedBody()['params'] : $request->getQueryParams()['params'];
         if ($params['action'] === 'getContextHelp') {
             $result = $this->getContextHelp($params['table'], $params['field']);
-            $response->getBody()->write(json_encode([
+            return GeneralUtility::makeInstance(JsonResponse::class, [
                 'title' => $result['title'],
                 'content' => $result['description'],
                 'link' => $result['moreInfo']
-            ]));
+            ]);
         }
         return $response;
     }
diff --git a/typo3/sysext/backend/Classes/Controller/ContextMenuController.php b/typo3/sysext/backend/Classes/Controller/ContextMenuController.php
index cc83c11c7545..71bf5ff69965 100644
--- a/typo3/sysext/backend/Classes/Controller/ContextMenuController.php
+++ b/typo3/sysext/backend/Classes/Controller/ContextMenuController.php
@@ -19,6 +19,7 @@ use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Clipboard\Clipboard;
 use TYPO3\CMS\Backend\ContextMenu\ContextMenu;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -39,10 +40,9 @@ class ContextMenuController
      * Renders a context menu
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function getContextMenuAction(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
+    public function getContextMenuAction(ServerRequestInterface $request): ResponseInterface
     {
         $contextMenu = GeneralUtility::makeInstance(ContextMenu::class);
 
@@ -52,16 +52,14 @@ class ContextMenuController
         if (!is_array($items)) {
             $items = [];
         }
-        $response->getBody()->write(json_encode($items));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($items);
     }
 
     /**
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function clipboardAction(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
+    public function clipboardAction(ServerRequestInterface $request): ResponseInterface
     {
         /** @var Clipboard $clipboard */
         $clipboard = GeneralUtility::makeInstance(Clipboard::class);
@@ -72,8 +70,7 @@ class ContextMenuController
         $clipboard->cleanCurrent();
 
         $clipboard->endClipboard();
-        $response->getBody()->write(json_encode([]));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload([]);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/File/FileController.php b/typo3/sysext/backend/Classes/Controller/File/FileController.php
index e7ef88e8b5f0..307bade7e93b 100644
--- a/typo3/sysext/backend/Classes/Controller/File/FileController.php
+++ b/typo3/sysext/backend/Classes/Controller/File/FileController.php
@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Backend\Controller\File;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Resource\DuplicationBehavior;
@@ -232,7 +233,7 @@ class FileController
                     }
                 }
             }
-            $response->getBody()->write(json_encode($flatResult));
+            return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($flatResult);
         }
         return $response;
     }
@@ -241,10 +242,9 @@ class FileController
      * Ajax entry point to check if a file exists in a folder
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function fileExistsInFolderAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function fileExistsInFolderAction(ServerRequestInterface $request)
     {
         $fileName = isset($request->getParsedBody()['fileName']) ? $request->getParsedBody()['fileName'] : $request->getQueryParams()['fileName'];
         $fileTarget = isset($request->getParsedBody()['fileTarget']) ? $request->getParsedBody()['fileTarget'] : $request->getQueryParams()['fileTarget'];
@@ -259,8 +259,7 @@ class FileController
         if ($fileTargetObject->hasFile($processedFileName)) {
             $result = $this->flattenResultDataValue($fileTargetObject->getStorage()->getFileInFolder($processedFileName, $fileTargetObject));
         }
-        $response->getBody()->write(json_encode($result));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload([$result]);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/FileSystemNavigationFrameController.php b/typo3/sysext/backend/Classes/Controller/FileSystemNavigationFrameController.php
index 143c87810b83..260da27c501e 100644
--- a/typo3/sysext/backend/Classes/Controller/FileSystemNavigationFrameController.php
+++ b/typo3/sysext/backend/Classes/Controller/FileSystemNavigationFrameController.php
@@ -20,6 +20,7 @@ use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Template\Components\ButtonBar;
 use TYPO3\CMS\Backend\Template\ModuleTemplate;
 use TYPO3\CMS\Backend\Tree\View\ElementBrowserFolderTreeView;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -228,17 +229,14 @@ class FileSystemNavigationFrameController
      * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function ajaxExpandCollapse(ServerRequestInterface $request, ResponseInterface $response)
+    public function ajaxExpandCollapse(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
     {
         $this->init();
         $tree = $this->foldertree->getBrowsableTree();
         if ($this->foldertree->getAjaxStatus() === false) {
-            $response = $response->withStatus(500);
-        } else {
-            $response->getBody()->write(json_encode($tree));
+            return $response->withStatus(500);
         }
-
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, $tree);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/FlashMessageController.php b/typo3/sysext/backend/Classes/Controller/FlashMessageController.php
index d4aaee47c2f4..ad3b620bb046 100644
--- a/typo3/sysext/backend/Classes/Controller/FlashMessageController.php
+++ b/typo3/sysext/backend/Classes/Controller/FlashMessageController.php
@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Backend\Controller;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -29,10 +30,9 @@ class FlashMessageController
      * Renders the FlashMessages from queue and returns them as JSON.
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function getQueuedFlashMessagesAction(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
+    public function getQueuedFlashMessagesAction(ServerRequestInterface $request): ResponseInterface
     {
         $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
         $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
@@ -47,7 +47,6 @@ class FlashMessageController
             ];
         }
 
-        $response->getBody()->write(json_encode($messages));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($messages);
     }
 }
diff --git a/typo3/sysext/backend/Classes/Controller/FormFlexAjaxController.php b/typo3/sysext/backend/Classes/Controller/FormFlexAjaxController.php
index f0ac0128e0c2..b4bf02160864 100644
--- a/typo3/sysext/backend/Classes/Controller/FormFlexAjaxController.php
+++ b/typo3/sysext/backend/Classes/Controller/FormFlexAjaxController.php
@@ -21,6 +21,7 @@ use TYPO3\CMS\Backend\Form\FormDataCompiler;
 use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
 use TYPO3\CMS\Backend\Form\NodeFactory;
 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\StringUtility;
@@ -34,10 +35,9 @@ class FormFlexAjaxController extends AbstractFormEngineAjaxController
      * Render a single flex form section container to add it to the DOM
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function containerAdd(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
+    public function containerAdd(ServerRequestInterface $request): ResponseInterface
     {
         $queryParameters = $request->getParsedBody();
 
@@ -180,8 +180,6 @@ class FormFlexAjaxController extends AbstractFormEngineAjaxController
         $requireJsModule = $this->createExecutableStringRepresentationOfRegisteredRequireJsModules($newContainerResult);
         $jsonResult['scriptCall'] = array_merge($requireJsModule, $jsonResult['scriptCall']);
 
-        $response->getBody()->write(json_encode($jsonResult));
-
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, $jsonResult);
     }
 }
diff --git a/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php b/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
index be1064f80c89..00ef626246b2 100644
--- a/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
+++ b/typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
@@ -23,6 +23,7 @@ use TYPO3\CMS\Backend\Form\InlineStackProcessor;
 use TYPO3\CMS\Backend\Form\NodeFactory;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Messaging\AbstractMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
@@ -38,10 +39,9 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
      * Create a new inline child via AJAX.
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function createAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function createAction(ServerRequestInterface $request): ResponseInterface
     {
         $ajaxArguments = isset($request->getParsedBody()['ajax']) ? $request->getParsedBody()['ajax'] : $request->getQueryParams()['ajax'];
         $parentConfig = $this->extractSignedParentConfigFromRequest((string)$ajaxArguments['context']);
@@ -162,19 +162,16 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
         // Fade out and fade in the new record in the browser view to catch the user's eye
         $jsonArray['scriptCall'][] = 'inline.fadeOutFadeIn(' . GeneralUtility::quoteJSvalue($objectId . '_div') . ');';
 
-        $response->getBody()->write(json_encode($jsonArray));
-
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, $jsonArray);
     }
 
     /**
      * Show the details of a child record.
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function detailsAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function detailsAction(ServerRequestInterface $request): ResponseInterface
     {
         $ajaxArguments = isset($request->getParsedBody()['ajax']) ? $request->getParsedBody()['ajax'] : $request->getQueryParams()['ajax'];
 
@@ -246,9 +243,7 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
             $jsonArray['scriptCall'][] = 'inline.collapseAllRecords(' . GeneralUtility::quoteJSvalue($objectId) . ',' . GeneralUtility::quoteJSvalue($objectPrefix) . ',\'' . (int)$child['uid'] . '\');';
         }
 
-        $response->getBody()->write(json_encode($jsonArray));
-
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, $jsonArray);
     }
 
     /**
@@ -256,10 +251,9 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
      * Handle AJAX calls to localize all records of a parent, localize a single record or to synchronize with the original language parent.
      *
      * @param ServerRequestInterface $request the incoming request
-     * @param ResponseInterface $response the empty response
      * @return ResponseInterface the filled response
      */
-    public function synchronizeLocalizeAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function synchronizeLocalizeAction(ServerRequestInterface $request): ResponseInterface
     {
         $ajaxArguments = isset($request->getParsedBody()['ajax']) ? $request->getParsedBody()['ajax'] : $request->getQueryParams()['ajax'];
         $domObjectId = $ajaxArguments[0];
@@ -273,7 +267,11 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
         $inlineStackProcessor->injectAjaxConfiguration($parentConfig);
         $inlineFirstPid = $this->getInlineFirstPidFromDomObjectId($domObjectId);
 
-        $jsonArray = false;
+        $jsonArray = [
+            'data' => '',
+            'stylesheetFiles' => [],
+            'scriptCall' => [],
+        ];
         if ($type === 'localize' || $type === 'synchronize' || MathUtility::canBeInterpretedAsInteger($type)) {
             // Parent, this table embeds the child table
             $parent = $inlineStackProcessor->getStructureLevel(-1);
@@ -339,11 +337,6 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
 
             $newItemList = $tce->registerDBList[$parent['table']][$parent['uid']][$parentFieldName];
 
-            $jsonArray = [
-                'data' => '',
-                'stylesheetFiles' => [],
-                'scriptCall' => [],
-            ];
             $nameObject = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($inlineFirstPid);
             $nameObjectForeignTable = $nameObject . '-' . $child['table'];
 
@@ -408,20 +401,16 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
                 . ', json.data);';
             }
         }
-
-        $response->getBody()->write(json_encode($jsonArray));
-
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($jsonArray);
     }
 
     /**
      * Store status of inline children expand / collapse state in backend user uC.
      *
      * @param ServerRequestInterface $request the incoming request
-     * @param ResponseInterface $response the empty response
      * @return ResponseInterface the filled response
      */
-    public function expandOrCollapseAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function expandOrCollapseAction(ServerRequestInterface $request): ResponseInterface
     {
         $ajaxArguments = isset($request->getParsedBody()['ajax']) ? $request->getParsedBody()['ajax'] : $request->getQueryParams()['ajax'];
         $domObjectId = $ajaxArguments[0];
@@ -461,9 +450,7 @@ class FormInlineAjaxController extends AbstractFormEngineAjaxController
                 $backendUser->writeUC();
             }
         }
-
-        $response->getBody()->write(json_encode([]));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload([]);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/FormSelectTreeAjaxController.php b/typo3/sysext/backend/Classes/Controller/FormSelectTreeAjaxController.php
index 39aaa2155692..66a82d6adf7b 100644
--- a/typo3/sysext/backend/Classes/Controller/FormSelectTreeAjaxController.php
+++ b/typo3/sysext/backend/Classes/Controller/FormSelectTreeAjaxController.php
@@ -19,6 +19,7 @@ use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Form\FormDataCompiler;
 use TYPO3\CMS\Backend\Form\FormDataGroup\TcaSelectTreeAjaxFieldData;
 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -30,11 +31,10 @@ class FormSelectTreeAjaxController
      * Returns json representing category tree
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @throws \RuntimeException
      * @return ResponseInterface
      */
-    public function fetchDataAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function fetchDataAction(ServerRequestInterface $request): ResponseInterface
     {
         $tableName = $request->getQueryParams()['tableName'];
         $fieldName = $request->getQueryParams()['fieldName'];
@@ -179,8 +179,6 @@ class FormSelectTreeAjaxController
         } else {
             $treeData = $formData['processedTca']['columns'][$fieldName]['config']['items'];
         }
-
-        $response->getBody()->write(json_encode($treeData));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($treeData);
     }
 }
diff --git a/typo3/sysext/backend/Classes/Controller/LinkBrowserController.php b/typo3/sysext/backend/Classes/Controller/LinkBrowserController.php
index 8b28d1558131..195addb427ae 100644
--- a/typo3/sysext/backend/Classes/Controller/LinkBrowserController.php
+++ b/typo3/sysext/backend/Classes/Controller/LinkBrowserController.php
@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Backend\Controller;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\LinkHandling\LinkService;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -81,10 +82,9 @@ class LinkBrowserController extends AbstractLinkBrowserController
      * This avoids to implement the encoding functionality again in JS for the browser.
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function encodeTypoLink(ServerRequestInterface $request, ResponseInterface $response)
+    public function encodeTypoLink(ServerRequestInterface $request): ResponseInterface
     {
         $typoLinkParts = $request->getQueryParams();
         if (isset($typoLinkParts['params'])) {
@@ -93,9 +93,7 @@ class LinkBrowserController extends AbstractLinkBrowserController
         }
 
         $typoLink = GeneralUtility::makeInstance(TypoLinkCodecService::class)->encode($typoLinkParts);
-
-        $response->getBody()->write(json_encode(['typoLink' => $typoLink]));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, ['typoLink' => $typoLink]);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/LiveSearchController.php b/typo3/sysext/backend/Classes/Controller/LiveSearchController.php
index ba2059843746..baa69f1bfda3 100644
--- a/typo3/sysext/backend/Classes/Controller/LiveSearchController.php
+++ b/typo3/sysext/backend/Classes/Controller/LiveSearchController.php
@@ -18,6 +18,7 @@ use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Search\LiveSearch\LiveSearch;
 use TYPO3\CMS\Backend\Search\LiveSearch\QueryParser;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -34,10 +35,9 @@ class LiveSearchController
      * Processes all AJAX calls and sends back a JSON object
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function liveSearchAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function liveSearchAction(ServerRequestInterface $request): ResponseInterface
     {
         $queryString = $request->getQueryParams()['q'];
         $liveSearch = GeneralUtility::makeInstance(LiveSearch::class);
@@ -59,7 +59,6 @@ class LiveSearchController
                 $searchResults[] = $item;
             }
         }
-        $response->getBody()->write(json_encode($searchResults));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($searchResults);
     }
 }
diff --git a/typo3/sysext/backend/Classes/Controller/OnlineMediaController.php b/typo3/sysext/backend/Classes/Controller/OnlineMediaController.php
index da17237dd0e2..23b5dc4093e1 100644
--- a/typo3/sysext/backend/Classes/Controller/OnlineMediaController.php
+++ b/typo3/sysext/backend/Classes/Controller/OnlineMediaController.php
@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Backend\Controller;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Http\RedirectResponse;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
@@ -34,10 +35,9 @@ class OnlineMediaController
      * AJAX endpoint for storing the URL as a sys_file record
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function createAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function createAction(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
     {
         $url = $request->getParsedBody()['url'];
         $targetFolderIdentifier = $request->getParsedBody()['targetFolder'];
@@ -51,7 +51,7 @@ class OnlineMediaController
             } else {
                 $data['error'] = $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:online_media.error.invalid_url');
             }
-            $response->getBody()->write(json_encode($data));
+            return GeneralUtility::makeInstance(JsonResponse::class, $data);
         }
         return $response;
     }
diff --git a/typo3/sysext/backend/Classes/Controller/Page/LocalizationController.php b/typo3/sysext/backend/Classes/Controller/Page/LocalizationController.php
index 5f9fabd0b6f9..548acdc2bd2c 100644
--- a/typo3/sysext/backend/Classes/Controller/Page/LocalizationController.php
+++ b/typo3/sysext/backend/Classes/Controller/Page/LocalizationController.php
@@ -20,6 +20,7 @@ use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
 use TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -110,8 +111,7 @@ class LocalizationController
             }
         }
 
-        $response->getBody()->write(json_encode($availableLanguages));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($availableLanguages);
     }
 
     /**
@@ -150,8 +150,7 @@ class LocalizationController
             ];
         }
 
-        $response->getBody()->write(json_encode($records));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($records);
     }
 
     /**
@@ -184,8 +183,7 @@ class LocalizationController
 
         $this->process($params);
 
-        $response->getBody()->write(json_encode([]));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload([]);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/SimpleDataHandlerController.php b/typo3/sysext/backend/Classes/Controller/SimpleDataHandlerController.php
index 71a0db5cc753..f9eafcfb0f85 100644
--- a/typo3/sysext/backend/Classes/Controller/SimpleDataHandlerController.php
+++ b/typo3/sysext/backend/Classes/Controller/SimpleDataHandlerController.php
@@ -19,6 +19,7 @@ use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Clipboard\Clipboard;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Messaging\AbstractMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -220,10 +221,9 @@ class SimpleDataHandlerController
      * Processes all AJAX calls and returns a JSON formatted string
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function processAjaxRequest(ServerRequestInterface $request, ResponseInterface $response)
+    public function processAjaxRequest(ServerRequestInterface $request): ResponseInterface
     {
         // do the regular / main logic
         $this->initClipboard();
@@ -254,9 +254,7 @@ class SimpleDataHandlerController
                 }
             }
         }
-
-        $response->getBody()->write(json_encode($content));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, $content);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/UserSettingsController.php b/typo3/sysext/backend/Classes/Controller/UserSettingsController.php
index 64cac5aad3df..cf7cbad9e11f 100644
--- a/typo3/sysext/backend/Classes/Controller/UserSettingsController.php
+++ b/typo3/sysext/backend/Classes/Controller/UserSettingsController.php
@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Backend\Controller;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -30,10 +31,9 @@ class UserSettingsController
      * Processes all AJAX calls and returns a JSON for the data
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function processAjaxRequest(ServerRequestInterface $request, ResponseInterface $response)
+    public function processAjaxRequest(ServerRequestInterface $request): ResponseInterface
     {
         // do the regular / main logic, depending on the action parameter
         $action = isset($request->getParsedBody()['action']) ? $request->getParsedBody()['action'] : $request->getQueryParams()['action'];
@@ -41,9 +41,7 @@ class UserSettingsController
         $value = isset($request->getParsedBody()['value']) ? $request->getParsedBody()['value'] : $request->getQueryParams()['value'];
 
         $content = $this->process($action, $key, $value);
-
-        $response->getBody()->write(json_encode($content));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($content);
     }
 
     /**
diff --git a/typo3/sysext/backend/Classes/Controller/Wizard/SuggestWizardController.php b/typo3/sysext/backend/Classes/Controller/Wizard/SuggestWizardController.php
index 10ae2c4fdfcf..4510555083fd 100644
--- a/typo3/sysext/backend/Classes/Controller/Wizard/SuggestWizardController.php
+++ b/typo3/sysext/backend/Classes/Controller/Wizard/SuggestWizardController.php
@@ -20,6 +20,7 @@ use TYPO3\CMS\Backend\Form\Wizard\SuggestWizardDefaultReceiver;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -32,11 +33,10 @@ class SuggestWizardController
      * Ajax handler for the "suggest" feature in FormEngine.
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @throws \RuntimeException for incomplete or invalid arguments
      * @return ResponseInterface
      */
-    public function searchAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function searchAction(ServerRequestInterface $request): ResponseInterface
     {
         $parsedBody = $request->getParsedBody();
 
@@ -161,9 +161,7 @@ class SuggestWizardController
         $maxItems = min(count($resultRows), $maxItems);
 
         array_splice($resultRows, $maxItems);
-
-        $response->getBody()->write(json_encode(array_values($resultRows)));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload(array_values($resultRows));
     }
 
     /**
diff --git a/typo3/sysext/documentation/Classes/Controller/DocumentController.php b/typo3/sysext/documentation/Classes/Controller/DocumentController.php
index 147bf05edf0c..adb02c926f90 100644
--- a/typo3/sysext/documentation/Classes/Controller/DocumentController.php
+++ b/typo3/sysext/documentation/Classes/Controller/DocumentController.php
@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Documentation\Controller;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\View\BackendTemplateView;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Page\PageRenderer;
@@ -178,10 +179,9 @@ class DocumentController extends ActionController
      * Delete documentation with given packageKey
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function deleteAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function deleteAction(ServerRequestInterface $request)
     {
         $basePath = 'typo3conf/Documentation/';
         $packageKey = $request->getParsedBody();
@@ -190,8 +190,7 @@ class DocumentController extends ActionController
             $this->addFlashMessage(LocalizationUtility::translate('deleteFailed', 'Documentation'), '', FlashMessage::ERROR);
         }
 
-        $response->getBody()->write(json_encode($isDirDeleted));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload([$isDirDeleted]);
     }
 
     /**
diff --git a/typo3/sysext/recycler/Classes/Controller/RecyclerAjaxController.php b/typo3/sysext/recycler/Classes/Controller/RecyclerAjaxController.php
index e5a442560355..c898ffd7548b 100644
--- a/typo3/sysext/recycler/Classes/Controller/RecyclerAjaxController.php
+++ b/typo3/sysext/recycler/Classes/Controller/RecyclerAjaxController.php
@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Recycler\Controller;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
@@ -61,10 +62,9 @@ class RecyclerAjaxController
      * The main dispatcher function. Collect data and prepare HTML output.
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function dispatch(ServerRequestInterface $request, ResponseInterface $response)
+    public function dispatch(ServerRequestInterface $request)
     {
         $extPath = ExtensionManagementUtility::extPath('recycler');
         /* @var $view StandaloneView */
@@ -148,8 +148,7 @@ class RecyclerAjaxController
                 ];
                 break;
         }
-        $response->getBody()->write(json_encode($content));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($content);
     }
 
     /**
diff --git a/typo3/sysext/t3editor/Classes/CodeCompletion.php b/typo3/sysext/t3editor/Classes/CodeCompletion.php
index ec5b0cfa691e..8d881149eb5f 100644
--- a/typo3/sysext/t3editor/Classes/CodeCompletion.php
+++ b/typo3/sysext/t3editor/Classes/CodeCompletion.php
@@ -15,6 +15,8 @@ namespace TYPO3\CMS\T3editor;
  */
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Http\JsonResponse;
+use TYPO3\CMS\Core\Http\Response;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -56,16 +58,15 @@ class CodeCompletion
      * @param int $pageId ID of the page
      * @return ResponseInterface
      */
-    protected function loadTemplates($pageId)
+    protected function loadTemplates($pageId): ResponseInterface
     {
-        /** @var \TYPO3\CMS\Core\Http\Response $response */
-        $response = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\Response::class);
+        $response = GeneralUtility::makeInstance(Response::class);
 
         // Check whether access is granted (only admin have access to sys_template records):
         if ($GLOBALS['BE_USER']->isAdmin()) {
             // Check whether there is a pageId given:
             if ($pageId) {
-                $response->getBody()->write(json_encode($this->getMergedTemplates($pageId)));
+                $response = GeneralUtility::makeInstance(JsonResponse::class)->setPayload($this->getMergedTemplates($pageId));
             } else {
                 $response->getBody()->write($GLOBALS['LANG']->getLL('pageIDInteger'));
                 $response = $response->withStatus(500);
diff --git a/typo3/sysext/t3editor/Classes/TypoScriptReferenceLoader.php b/typo3/sysext/t3editor/Classes/TypoScriptReferenceLoader.php
index 8dd0a8696534..7a58c7375b0f 100644
--- a/typo3/sysext/t3editor/Classes/TypoScriptReferenceLoader.php
+++ b/typo3/sysext/t3editor/Classes/TypoScriptReferenceLoader.php
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\T3editor;
  */
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -40,15 +41,13 @@ class TypoScriptReferenceLoader
      * Called by AjaxRequestHandler
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function processAjaxRequest(ServerRequestInterface $request, ResponseInterface $response)
+    public function processAjaxRequest(ServerRequestInterface $request): ResponseInterface
     {
         // Load the TSref XML information:
         $this->loadFile(GeneralUtility::getFileAbsFileName('EXT:t3editor/Resources/Private/tsref.xml'));
-        $response->getBody()->write(json_encode($this->getTypes()));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($this->getTypes());
     }
 
     /**
diff --git a/typo3/sysext/workspaces/Classes/Controller/AjaxController.php b/typo3/sysext/workspaces/Classes/Controller/AjaxController.php
index 7738a75cc79c..e62406117b70 100644
--- a/typo3/sysext/workspaces/Classes/Controller/AjaxController.php
+++ b/typo3/sysext/workspaces/Classes/Controller/AjaxController.php
@@ -17,6 +17,8 @@ namespace TYPO3\CMS\Workspaces\Controller;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Http\JsonResponse;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Implements the AJAX functionality for the various asynchronous calls
@@ -28,10 +30,9 @@ class AjaxController
      * called by the Backend toolbar menu
      *
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function switchWorkspaceAction(ServerRequestInterface $request, ResponseInterface $response)
+    public function switchWorkspaceAction(ServerRequestInterface $request): ResponseInterface
     {
         $parsedBody = $request->getParsedBody();
         $queryParams = $request->getQueryParams();
@@ -68,8 +69,7 @@ class AjaxController
             'workspaceId' => $workspaceId,
             'pageId'      => ($finalPageUid && $originalPageId == $finalPageUid) ? null : $finalPageUid
         ];
-        $response->getBody()->write(json_encode($ajaxResponse));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class, $ajaxResponse);
     }
 
     /**
diff --git a/typo3/sysext/workspaces/Classes/Controller/AjaxDispatcher.php b/typo3/sysext/workspaces/Classes/Controller/AjaxDispatcher.php
index 2f16e04088b8..713e5ae1583d 100644
--- a/typo3/sysext/workspaces/Classes/Controller/AjaxDispatcher.php
+++ b/typo3/sysext/workspaces/Classes/Controller/AjaxDispatcher.php
@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Workspaces\Controller;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Http\JsonResponse;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Workspaces\Controller\Remote\ActionHandler;
 use TYPO3\CMS\Workspaces\Controller\Remote\MassActionHandler;
@@ -37,10 +38,9 @@ class AjaxDispatcher
 
     /**
      * @param ServerRequestInterface $request
-     * @param ResponseInterface $response
      * @return ResponseInterface
      */
-    public function dispatch(ServerRequestInterface $request, ResponseInterface $response)
+    public function dispatch(ServerRequestInterface $request): ResponseInterface
     {
         $callStack = \GuzzleHttp\json_decode($request->getBody()->getContents());
         if (!is_array($callStack)) {
@@ -54,8 +54,7 @@ class AjaxDispatcher
             $instance = GeneralUtility::makeInstance($className);
             $results[] = $this->buildResultFromResponse(call_user_func_array([$instance, $method], $parameters), $call);
         }
-        $response->getBody()->write(json_encode($results));
-        return $response;
+        return GeneralUtility::makeInstance(JsonResponse::class)->setPayload($results);
     }
 
     /**
-- 
GitLab