I spoke to @christianm again and we talked about a backwards compatible(!) solution for the next LTS that already gives people the chance to adjust their code to (or at least get acquainted with) the “new way of doing things” with Neos 5.0:
- We don’t add
HttpRequestHandlerInterface::getComponentContext()
just yet¹, but instead only deprecateHttpRequestHandlerInterface:: getHttpRequest()
andHttpRequestHandlerInterface:: getHttpResponse()
with an explanation to work around this dependency (see below) - We add another method
setComponentParameter()
to theActionResponse
that allows for “unplanned extensibility”.
The resulting signature of the ActionResponse
would then be sth like:
final class ActionResponse
{
public function setContent(string $content): void
public function setContentType(string $contentType): void
public function setRedirectUri(UriInterface $uri, int $statusCode = 303): void
public function setStatusCode(int $statusCode): void
/** new **/
public function setComponentParameter(string $componentClassName, string $parameterName, $value): void
}
With that in place, people could still mess with the HTTP request/response as much as they want, but via the ActionResponse
:
$this->response->setComponentParameter(MyComponent::class, 'setMySpecialHeader', 'foo');
and write a corresponding HTTP Component:
final class MyComponent implements ComponentInterface
{
public function handle(ComponentContext $componentContext)
{
$specialHeader = $componentContext->getParameter(static::class, 'setMySpecialHeader');
if ($specialHeader !== null) {
$modifiedResponse = $componentContext->getHttpResponse()->withAddedHeader('X-My-Header', $specialHeader);
$componentContext->replaceHttpResponse($modifiedResponse);
}
}
}
And, in response to some of @aberl’s comments:
Yes, good point, but I still think that we should not encourage that kind of architecture: For middleware kind of cases MVC is not the matching pattern IMO, a custom HTTP component is much better suited.
Having said that, with the above it would be possible to replace the whole Response ofc:
$this->response->setComponentParameter(MyComponent::class, 'replaceHttpResponse', $myHttpResponse);
Sorry, I should have explained myself a bit clearer.
Controller::action(ActionRequest): ActionResponse
would make a lot of sense and in case of a redirect that could be a special RedirectResponse
like you suggested.
But we currently have
Dispatcher::dispatch(RequestInterface $request, ResponseInterface $response): void
currently and solve redirects via an endless loop and exceptions.
I’m all for changing that at some point, but it would be yet another major breaking change.
¹ I don’t think that we need to re-introduce a way to get hold of the HTTP request/response or ComponentContext at all. With the extension mentioned above this should not be required. And (because it’s no interface) we could always extend the ActionResponse
to provide more features out of the box (like “E-Tag”, “Content lifetime”, …)