Defining global middleware (as per the middlewares documentation) which invokes a controller that relies on Controller::curr() triggers error No current controller available
YAML
SilverStripe\Core\Injector\Injector:
SilverStripe\Control\Director:
properties:
Middlewares:
CustomMiddleware: %$App\Middleware\CustomMiddleware
CustomMiddleware.php
<?php
namespace App\Middleware;
use App\Control\CustomController;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\Middleware\HTTPMiddleware;
class CustomMiddleware implements HTTPMiddleware
{
/**
* @param HTTPRequest $request
* @param callable $delegate
* @return HTTPResponse
*/
public function process(HTTPRequest $request, callable $delegate): HTTPResponse
{
return Injector::inst()->create(CustomController::class)->handleRequest($request);
}
}
CustomController.php
<?php
namespace App\Control;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
class CustomController extends Controller
{
/**
* {@inheritdoc}
*
* @throws \SilverStripe\Control\HTTPResponse_Exception
*/
public function handleRequest(HTTPRequest $request)
{
// Throw an error exception
$this->httpError(404);
}
}
No current controller available
Controller.php:556
SilverStripe\Control\Controller::curr()
ErrorPage.php:106
SilverStripe\ErrorPage\ErrorPage::response_for(404)
ErrorPageControllerExtension.php:29
SilverStripe\ErrorPage\ErrorPageControllerExtension->onBeforeHTTPError(404, SilverStripe\Control\NullHTTPRequest, , , , , )
Extensible.php:468
SilverStripe\View\ViewableData->extend(onBeforeHTTPError, 404, SilverStripe\Control\NullHTTPRequest, )
RequestHandler.php:524
SilverStripe\Control\RequestHandler->httpError(404)
CustomController.php:19
This is a valid state of the application. A controller is decided during the main application flow (the inner-most layer of the middleware stack). The controller is then unselected at the end of that flow. Having a controller available with Controller::curr would be a bug imo.
As an example of what could be done here - you could adjust the routing information in a middleware based on something in the request - meaning a middleware can help delegate the request to specific controllers - for instance a middleware than handles Accept headers, if you choose to handle different response content types with different controllers.
I'm not sure if that would work for you, but it may become available after you delegate the request to the Director.
E.g.:
// Controller::curr() unavailable
$result = $delegate($request);
$controller = Controller::curr(); // should be available here
// ... do something ...
return $result;
P.S. currently it appears that we actually do routing before passing control to the middlewares, so we could potentially pass that info as well (or initialise Controller::curr). However, from this issue description it's not exactly clear what are the potential use cases for that .
If the controller state persists after delegating the request, I would consider that a bug.
The controller should be de-allocated after handling the request: https://github.com/silverstripe/silverstripe-framework/blob/f842ee2eec984002fce944157837251fea193b20/src/Control/Controller.php#L219
Sorry @jakxnz . I'm going to close this as expected behaviour. Feel free to discuss further and we can re-open if required.
Most helpful comment
Sorry @jakxnz . I'm going to close this as expected behaviour. Feel free to discuss further and we can re-open if required.