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
|
Name: requestprocessors
|
||||||
---
|
---
|
||||||
|
SilverStripe\Core\Injector\Injector:
|
||||||
SilverStripe\Control\Director:
|
SilverStripe\Control\Director:
|
||||||
middlewares:
|
properties:
|
||||||
|
Middlewares:
|
||||||
TrustedProxyMiddleware: '%$SilverStripe\Control\TrustedProxyMiddleware'
|
TrustedProxyMiddleware: '%$SilverStripe\Control\TrustedProxyMiddleware'
|
||||||
AllowedHostsMiddleware: '%$SilverStripe\Control\AllowedHostsMiddleware'
|
AllowedHostsMiddleware: '%$SilverStripe\Control\AllowedHostsMiddleware'
|
||||||
SessionMiddleware: 'SilverStripe\Control\SessionMiddleware'
|
SessionMiddleware: '%$SilverStripe\Control\SessionMiddleware'
|
||||||
RequestProcessor: 'SilverStripe\Control\RequestProcessor'
|
RequestProcessor: '%$SilverStripe\Control\RequestProcessor'
|
||||||
FlushMiddleware: '%$SilverStripe\Control\FlushMiddleware'
|
FlushMiddleware: '%$SilverStripe\Control\FlushMiddleware'
|
||||||
|
|
||||||
|
|
||||||
SilverStripe\Core\Injector\Injector:
|
|
||||||
SilverStripe\Control\AllowedHostsMiddleware:
|
SilverStripe\Control\AllowedHostsMiddleware:
|
||||||
properties:
|
properties:
|
||||||
AllowedHosts: "`SS_ALLOWED_HOSTS`"
|
AllowedHosts: "`SS_ALLOWED_HOSTS`"
|
||||||
|
@ -19,10 +19,11 @@ SilverStripe\Core\Injector\Injector:
|
|||||||
---
|
---
|
||||||
Name: coresecurity
|
Name: coresecurity
|
||||||
---
|
---
|
||||||
SilverStripe\Control\Director:
|
|
||||||
middlewares:
|
|
||||||
- %$SilverStripe\Security\AuthenticationMiddleware
|
|
||||||
SilverStripe\Core\Injector\Injector:
|
SilverStripe\Core\Injector\Injector:
|
||||||
|
SilverStripe\Control\Director:
|
||||||
|
properties:
|
||||||
|
Middlewares:
|
||||||
|
- %$SilverStripe\Security\AuthenticationMiddleware
|
||||||
SilverStripe\Security\AuthenticationMiddleware:
|
SilverStripe\Security\AuthenticationMiddleware:
|
||||||
properties:
|
properties:
|
||||||
AuthenticationHandler: %$SilverStripe\Security\AuthenticationHandler
|
AuthenticationHandler: %$SilverStripe\Security\AuthenticationHandler
|
||||||
|
@ -29,6 +29,7 @@ use SilverStripe\View\TemplateGlobalProvider;
|
|||||||
class Director implements TemplateGlobalProvider
|
class Director implements TemplateGlobalProvider
|
||||||
{
|
{
|
||||||
use Configurable;
|
use Configurable;
|
||||||
|
use HTTPMiddlewareAware;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies this url is relative to the base.
|
* Specifies this url is relative to the base.
|
||||||
@ -132,7 +133,7 @@ class Director implements TemplateGlobalProvider
|
|||||||
) {
|
) {
|
||||||
return static::mockRequest(
|
return static::mockRequest(
|
||||||
function (HTTPRequest $request) {
|
function (HTTPRequest $request) {
|
||||||
return static::handleRequest($request);
|
return Injector::inst()->get(Director::class)->handleRequest($request);
|
||||||
},
|
},
|
||||||
$url,
|
$url,
|
||||||
$postVars,
|
$postVars,
|
||||||
@ -302,15 +303,12 @@ class Director implements TemplateGlobalProvider
|
|||||||
* @return HTTPResponse
|
* @return HTTPResponse
|
||||||
* @throws HTTPResponse_Exception
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
public static function handleRequest(HTTPRequest $request)
|
public function handleRequest(HTTPRequest $request)
|
||||||
{
|
{
|
||||||
Injector::inst()->registerService($request, HTTPRequest::class);
|
Injector::inst()->registerService($request, HTTPRequest::class);
|
||||||
|
|
||||||
$rules = Director::config()->uninherited('rules');
|
$rules = Director::config()->uninherited('rules');
|
||||||
|
|
||||||
// Get global middlewares
|
|
||||||
$middlewares = Director::config()->uninherited('middlewares') ?: [];
|
|
||||||
|
|
||||||
// Default handler - mo URL rules matched, so return a 404 error.
|
// Default handler - mo URL rules matched, so return a 404 error.
|
||||||
$handler = function () {
|
$handler = function () {
|
||||||
return new HTTPResponse('No URL rule was matched', 404);
|
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
|
// Match pattern
|
||||||
$arguments = $request->match($pattern, true);
|
$arguments = $request->match($pattern, true);
|
||||||
if ($arguments !== false) {
|
if ($arguments !== false) {
|
||||||
@ -363,12 +349,25 @@ class Director implements TemplateGlobalProvider
|
|||||||
|
|
||||||
// Find the controller name
|
// Find the controller name
|
||||||
$controller = $arguments['Controller'];
|
$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 for calling a controller
|
||||||
$handler = function ($request) use ($controllerObj) {
|
$handler = function ($request) use ($controllerObj) {
|
||||||
try {
|
try {
|
||||||
|
// 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);
|
return $controllerObj->handleRequest($request);
|
||||||
|
});
|
||||||
} catch (HTTPResponse_Exception $responseException) {
|
} catch (HTTPResponse_Exception $responseException) {
|
||||||
return $responseException->getResponse();
|
return $responseException->getResponse();
|
||||||
}
|
}
|
||||||
@ -377,12 +376,8 @@ class Director implements TemplateGlobalProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the handler with the given middlewares
|
// Call the handler with the configured middlewares
|
||||||
$response = self::callWithMiddlewares(
|
$response = $this->callMiddleware($request, $handler);
|
||||||
$request,
|
|
||||||
$middlewares,
|
|
||||||
$handler
|
|
||||||
);
|
|
||||||
|
|
||||||
// Note that if a different request was previously registered, this will now be lost
|
// 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
|
// In these cases it's better to use Kernel::nest() prior to kicking off a nested request
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace SilverStripe\Control;
|
namespace SilverStripe\Control;
|
||||||
|
|
||||||
use SilverStripe\Core\Application;
|
use SilverStripe\Core\Application;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Control\HTTPMiddleware;
|
use SilverStripe\Control\HTTPMiddleware;
|
||||||
use SilverStripe\Core\Kernel;
|
use SilverStripe\Core\Kernel;
|
||||||
|
|
||||||
@ -11,10 +12,8 @@ use SilverStripe\Core\Kernel;
|
|||||||
*/
|
*/
|
||||||
class HTTPApplication implements Application
|
class HTTPApplication implements Application
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var HTTPMiddleware[]
|
use HTTPMiddlewareAware;
|
||||||
*/
|
|
||||||
protected $middlewares = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Kernel
|
* @var Kernel
|
||||||
@ -26,54 +25,6 @@ class HTTPApplication implements Application
|
|||||||
$this->kernel = $kernel;
|
$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
|
* Get the kernel for this application
|
||||||
*
|
*
|
||||||
@ -96,7 +47,7 @@ class HTTPApplication implements Application
|
|||||||
|
|
||||||
// Ensure boot is invoked
|
// Ensure boot is invoked
|
||||||
return $this->execute($request, function (HTTPRequest $request) {
|
return $this->execute($request, function (HTTPRequest $request) {
|
||||||
return Director::handleRequest($request);
|
return Injector::inst()->get(Director::class)->handleRequest($request);
|
||||||
}, $flush);
|
}, $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
|
class RequestHandler extends ViewableData
|
||||||
{
|
{
|
||||||
|
|
||||||
|
use HTTPMiddlewareAware;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional url_segment for this request handler
|
* Optional url_segment for this request handler
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user