Extbase controller action responses in TYPO3 v11+
TYPO3 v11
In TYPO3 v11.0 the change was introduced that actions should return a PSR-7 response, it is deprecated not to do this. So if you want your extension to work in TYPO3 v10 and TYPO3 v11, leave it as it is for now.
However, if your extension only supports TYPO3 v11 and above, you should always return the PSR-7 response.
Behaviour before TYPO3 v11
Before TYPO3 v11, the developer usually did not return anything in an Extbase action:
public function listAction()
{
$records = $this->recordRepository->findAll();
$this->view->assign('records', $records);
}
However, you can also return the rendered content (usually a string) to be displayed on the web page, but this is optional:
public function listAction()
{
$records = $this->recordRepository->findAll();
$this->view->assign('records', $records);
return $this->view->render();
}
Behaviour beginning with TYPO3 v11
With the release of TYPO3 v11.0 a \Psr\Http\Message\ResponseInterface
should be returned.
Return HTML as response
The most common request is to return HTML content:
public function listAction(): \Psr\Http\Message\ResponseInterface
{
$records = $this->recordRepository->findAll();
$this->view->assign('records', $records);
return $this->htmlResponse();
}
The htmlResponse()
method ensures that the view's render()
method is called. However, it can be called with the content to be displayed as an argument:
public function listAction(): Psr\Http\Message\ResponseInterface
{
$records = $this->recordRepository->findAll();
$this->view->assign('records', $records);
return $this->htmlResponse($this->view->render());
}
Let's take a look into the htmlResponse()
method of the \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
:
protected function htmlResponse(string $html = null): \Psr\Http\Message\ResponseInterface
{
return $this->responseFactory->createResponse()
->withHeader('Content-Type', 'text/html; charset=utf-8')
->withBody($this->streamFactory->createStream($html ?? $this->view->render()));
}
A PSR-7 response factory implementation is used to create the response. The content type header is set to text/html
and the body to the content (with the help of a PSR-7 stream factory implementation). So the htmlResponse()
method is only a convenient way to return HTML content.
The responseFactory
and the streamFactory
properties are available in any Extbase controller which extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
. We'll come back to this later where this can be useful.
Return JSON as response
Since returning JSON is also a common use case, the handy jsonResponse()
method is also available:
public function listAction(): \Psr\Http\Message\ResponseInterface
{
// ... build JSON
return $this->jsonResponse($json);
}
Like the htmlResponse()
, the jsonResponse()
accepts an optional parameter. If it is omitted, the view's render()
method is called.
Forward a response to another action
To forward a response, create an instance of the \TYPO3\CMS\Extbase\Http\ForwardResponse
class and add the necessary arguments via the with*
methods. Note that a PSR response is always immutable, so you always get back a new instance of the class:
public function listAction(): \Psr\Http\Message\ResponseInterface
{
return (new \TYPO3\CMS\Extbase\Http\ForwardResponse('list'))
->withExtensionName('AnotherExtension')
->withControllerName('AnotherController')
->withArguments(['parameter' => 'value']);
}
Redirect to another URL
To create a redirect to another URL, you have to create the response object yourself. In the example, it is assumed that you want to redirect to another action, so the URI builder is used. We create the response using a PSR-17 response factory implementation, which is already available in controllers that extend \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
.
public function listAction(): \Psr\Http\Message\ResponseInterface
{
// ... do something
$uri = $this->uriBuilder->uriFor('show', ['parameter' => $value]);
return $this->responseFactory->createResponse(307)
->withHeader('Location', $uri);
}
In this example, we create a temporary redirect (status code 307) and add the Location
header with the URL as the value.
Provide a download
Downloading a file when calling an action is different from the previous examples because the returned response object is processed via the page renderer by default. After creating the response object, we need to throw a \TYPO3\CMS\Core\Http\PropagateResponseException
to avoid this and return the response early. The exception code is the HTTP status code to use (in this case 200).
public function pdfAction(): \Psr\Http\Message\ResponseInterface
{
// ... do something
$response = $this->responseFactory->createResponse()
->withHeader('Cache-Control', 'private')
->withHeader('Content-Disposition', sprintf('attachment; filename="%s"', $filename))
->withHeader('Content-Length', (string)filesize($filePath))
->withHeader('Content-Type', 'application/pdf')
->withBody($this->streamFactory->createStreamFromFile($filePath));
throw new PropagateResponseException($response, 200);
}