mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API: Director::handleRequest() is no longer static - use a Director service
NEW: Add HTMLMiddlewareAware trait to HTTPApplication, Director, and RequestHandler NEW: Allow service specs to be passed to Director rules. This refactor of the controller middlewares takes a service definition approach rather than a static-method-and-config approach that Director historically had. The use of a trait for middleware means that the Middlewares array property can be defined on RequestHandler, Director, and HTTPApplication objects in the same way.
This commit is contained in:
parent
e92c63c545
commit
69fe166897
@ -1,16 +1,16 @@
|
||||
---
|
||||
Name: requestprocessors
|
||||
---
|
||||
SilverStripe\Control\Director:
|
||||
middlewares:
|
||||
TrustedProxyMiddleware: '%$SilverStripe\Control\TrustedProxyMiddleware'
|
||||
AllowedHostsMiddleware: '%$SilverStripe\Control\AllowedHostsMiddleware'
|
||||
SessionMiddleware: 'SilverStripe\Control\SessionMiddleware'
|
||||
RequestProcessor: 'SilverStripe\Control\RequestProcessor'
|
||||
FlushMiddleware: '%$SilverStripe\Control\FlushMiddleware'
|
||||
|
||||
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\Control\Director:
|
||||
properties:
|
||||
Middlewares:
|
||||
TrustedProxyMiddleware: '%$SilverStripe\Control\TrustedProxyMiddleware'
|
||||
AllowedHostsMiddleware: '%$SilverStripe\Control\AllowedHostsMiddleware'
|
||||
SessionMiddleware: '%$SilverStripe\Control\SessionMiddleware'
|
||||
RequestProcessor: '%$SilverStripe\Control\RequestProcessor'
|
||||
FlushMiddleware: '%$SilverStripe\Control\FlushMiddleware'
|
||||
|
||||
SilverStripe\Control\AllowedHostsMiddleware:
|
||||
properties:
|
||||
AllowedHosts: "`SS_ALLOWED_HOSTS`"
|
||||
|
@ -19,10 +19,11 @@ SilverStripe\Core\Injector\Injector:
|
||||
---
|
||||
Name: coresecurity
|
||||
---
|
||||
SilverStripe\Control\Director:
|
||||
middlewares:
|
||||
- %$SilverStripe\Security\AuthenticationMiddleware
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\Control\Director:
|
||||
properties:
|
||||
Middlewares:
|
||||
- %$SilverStripe\Security\AuthenticationMiddleware
|
||||
SilverStripe\Security\AuthenticationMiddleware:
|
||||
properties:
|
||||
AuthenticationHandler: %$SilverStripe\Security\AuthenticationHandler
|
||||
|
@ -29,6 +29,7 @@ use SilverStripe\View\TemplateGlobalProvider;
|
||||
class Director implements TemplateGlobalProvider
|
||||
{
|
||||
use Configurable;
|
||||
use HTTPMiddlewareAware;
|
||||
|
||||
/**
|
||||
* Specifies this url is relative to the base.
|
||||
@ -132,7 +133,7 @@ class Director implements TemplateGlobalProvider
|
||||
) {
|
||||
return static::mockRequest(
|
||||
function (HTTPRequest $request) {
|
||||
return static::handleRequest($request);
|
||||
return Injector::inst()->get(Director::class)->handleRequest($request);
|
||||
},
|
||||
$url,
|
||||
$postVars,
|
||||
@ -302,15 +303,12 @@ class Director implements TemplateGlobalProvider
|
||||
* @return HTTPResponse
|
||||
* @throws HTTPResponse_Exception
|
||||
*/
|
||||
public static function handleRequest(HTTPRequest $request)
|
||||
public function handleRequest(HTTPRequest $request)
|
||||
{
|
||||
Injector::inst()->registerService($request, HTTPRequest::class);
|
||||
|
||||
$rules = Director::config()->uninherited('rules');
|
||||
|
||||
// Get global middlewares
|
||||
$middlewares = Director::config()->uninherited('middlewares') ?: [];
|
||||
|
||||
// Default handler - mo URL rules matched, so return a 404 error.
|
||||
$handler = function () {
|
||||
return new HTTPResponse('No URL rule was matched', 404);
|
||||
@ -326,18 +324,6 @@ class Director implements TemplateGlobalProvider
|
||||
}
|
||||
}
|
||||
|
||||
// Add controller-specific middlewares
|
||||
if (isset($controllerOptions['Middlewares'])) {
|
||||
// Force to array
|
||||
if (!is_array($controllerOptions['Middlewares'])) {
|
||||
$controllerOptions['Middlewares'] = [$controllerOptions['Middlewares']];
|
||||
}
|
||||
$middlewares = array_merge($middlewares, $controllerOptions['Middlewares']);
|
||||
}
|
||||
|
||||
// Remove null middlewares (may be included due to limitatons of config yml)
|
||||
$middlewares = array_filter($middlewares);
|
||||
|
||||
// Match pattern
|
||||
$arguments = $request->match($pattern, true);
|
||||
if ($arguments !== false) {
|
||||
@ -363,12 +349,25 @@ class Director implements TemplateGlobalProvider
|
||||
|
||||
// Find the controller name
|
||||
$controller = $arguments['Controller'];
|
||||
$controllerObj = Injector::inst()->create($controller);
|
||||
|
||||
// String = service name
|
||||
if (is_string($controller)) {
|
||||
$controllerObj = Injector::inst()->get($controller);
|
||||
// Array = service spec
|
||||
} elseif (is_array($controller)) {
|
||||
$controllerObj = Injector::inst()->createFromSpec($controller);
|
||||
} else {
|
||||
throw new \LogicException("Invalid Controller value '$controller'");
|
||||
}
|
||||
|
||||
// Handler for calling a controller
|
||||
$handler = function ($request) use ($controllerObj) {
|
||||
try {
|
||||
return $controllerObj->handleRequest($request);
|
||||
// Apply the controller's middleware. We do this outside of handleRequest so that
|
||||
// subclasses of handleRequest will be called after the middlware processing
|
||||
return $controllerObj->callMiddleware($request, function ($request) use ($controllerObj) {
|
||||
return $controllerObj->handleRequest($request);
|
||||
});
|
||||
} catch (HTTPResponse_Exception $responseException) {
|
||||
return $responseException->getResponse();
|
||||
}
|
||||
@ -377,12 +376,8 @@ class Director implements TemplateGlobalProvider
|
||||
}
|
||||
}
|
||||
|
||||
// Call the handler with the given middlewares
|
||||
$response = self::callWithMiddlewares(
|
||||
$request,
|
||||
$middlewares,
|
||||
$handler
|
||||
);
|
||||
// Call the handler with the configured middlewares
|
||||
$response = $this->callMiddleware($request, $handler);
|
||||
|
||||
// Note that if a different request was previously registered, this will now be lost
|
||||
// In these cases it's better to use Kernel::nest() prior to kicking off a nested request
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace SilverStripe\Control;
|
||||
|
||||
use SilverStripe\Core\Application;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Control\HTTPMiddleware;
|
||||
use SilverStripe\Core\Kernel;
|
||||
|
||||
@ -11,10 +12,8 @@ use SilverStripe\Core\Kernel;
|
||||
*/
|
||||
class HTTPApplication implements Application
|
||||
{
|
||||
/**
|
||||
* @var HTTPMiddleware[]
|
||||
*/
|
||||
protected $middlewares = [];
|
||||
|
||||
use HTTPMiddlewareAware;
|
||||
|
||||
/**
|
||||
* @var Kernel
|
||||
@ -26,54 +25,6 @@ class HTTPApplication implements Application
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HTTPMiddleware[]
|
||||
*/
|
||||
public function getMiddlewares()
|
||||
{
|
||||
return $this->middlewares;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HTTPMiddleware[] $middlewares
|
||||
* @return $this
|
||||
*/
|
||||
public function setMiddlewares($middlewares)
|
||||
{
|
||||
$this->middlewares = $middlewares;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HTTPMiddleware $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function addMiddleware(HTTPMiddleware $middleware)
|
||||
{
|
||||
$this->middlewares[] = $middleware;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call middleware
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
* @param callable $last Last config to call
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
protected function callMiddleware(HTTPRequest $request, $last)
|
||||
{
|
||||
// Reverse middlewares
|
||||
$next = $last;
|
||||
/** @var HTTPMiddleware $middleware */
|
||||
foreach (array_reverse($this->getMiddlewares()) as $middleware) {
|
||||
$next = function ($request) use ($middleware, $next) {
|
||||
return $middleware->process($request, $next);
|
||||
};
|
||||
}
|
||||
return call_user_func($next, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the kernel for this application
|
||||
*
|
||||
@ -96,7 +47,7 @@ class HTTPApplication implements Application
|
||||
|
||||
// Ensure boot is invoked
|
||||
return $this->execute($request, function (HTTPRequest $request) {
|
||||
return Director::handleRequest($request);
|
||||
return Injector::inst()->get(Director::class)->handleRequest($request);
|
||||
}, $flush);
|
||||
}
|
||||
|
||||
|
64
src/Control/HTTPMiddlewareAware.php
Normal file
64
src/Control/HTTPMiddlewareAware.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Control;
|
||||
|
||||
/**
|
||||
* Adds middleware support to an object.
|
||||
* Provides a Middlewares property and a callMiddleware() callback
|
||||
*/
|
||||
trait HTTPMiddlewareAware
|
||||
{
|
||||
/**
|
||||
* @var HTTPMiddleware[]
|
||||
*/
|
||||
protected $middlewares = [];
|
||||
|
||||
/**
|
||||
* @return HTTPMiddleware[]
|
||||
*/
|
||||
public function getMiddlewares()
|
||||
{
|
||||
return $this->middlewares;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HTTPMiddleware[] $middlewares
|
||||
* @return $this
|
||||
*/
|
||||
public function setMiddlewares($middlewares)
|
||||
{
|
||||
// Allow nulls in the middlewares array to deal with limitations of yml config
|
||||
$this->middlewares = array_filter((array)$middlewares);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HTTPMiddleware $middleware
|
||||
* @return $this
|
||||
*/
|
||||
public function addMiddleware(HTTPMiddleware $middleware)
|
||||
{
|
||||
$this->middlewares[] = $middleware;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call middleware
|
||||
*
|
||||
* @param $request The request to pass to the middlewares and callback
|
||||
* @param $last The callback to call after all middlewares
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
public function callMiddleware(HTTPRequest $request, callable $last)
|
||||
{
|
||||
// Reverse middlewares
|
||||
$next = $last;
|
||||
/** @var HTTPMiddleware $middleware */
|
||||
foreach (array_reverse($this->getMiddlewares()) as $middleware) {
|
||||
$next = function ($request) use ($middleware, $next) {
|
||||
return $middleware->process($request, $next);
|
||||
};
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@ -46,6 +46,9 @@ use BadMethodCallException;
|
||||
*/
|
||||
class RequestHandler extends ViewableData
|
||||
{
|
||||
|
||||
use HTTPMiddlewareAware;
|
||||
|
||||
/**
|
||||
* Optional url_segment for this request handler
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user