diff --git a/docs/en/02_Developer_Guides/02_Controllers/02_Routing.md b/docs/en/02_Developer_Guides/02_Controllers/02_Routing.md index 832ef4b4f..b22c3a6b4 100644 --- a/docs/en/02_Developer_Guides/02_Controllers/02_Routing.md +++ b/docs/en/02_Developer_Guides/02_Controllers/02_Routing.md @@ -216,6 +216,30 @@ Director: 'feed': 'FeedController' ``` +## Root URL Handlers + +In some cases, the Director rule covers the entire URL you intend to match, and you simply want the controller to respond to a 'root' request. This request will automatically direct to an `index()` method if it exists on the controller, but you can also set a custom method to use in `$url_handlers` with the `'/'` key: + +```php +use SilverStripe\Control\Controller; + +class BreadAPIController extends Controller +{ + private static $allowed_actions = [ + 'getBreads', + 'createBread', + ]; + + private static $url_handlers = [ + 'GET /' => 'getBreads', + 'POST /' => 'createBread', + ]; +``` + +
+In SilverStripe Framework versions prior to 4.6, an empty key (`''`) must be used in place of the `'/'` key. When specifying an HTTP method, the empty string must be separated from the method (e.g. `'GET '`). The empty key and slash key are also equivalent in Director rules. +
+ ## Related Lessons * [Creating filtered views](https://www.silverstripe.org/learn/lessons/v4/creating-filtered-views-1) * [Controller actions / DataObjects as pages](https://www.silverstripe.org/learn/lessons/v4/controller-actions-dataobjects-as-pages-1) diff --git a/src/Control/HTTPRequest.php b/src/Control/HTTPRequest.php index 56680464b..46743c045 100644 --- a/src/Control/HTTPRequest.php +++ b/src/Control/HTTPRequest.php @@ -523,8 +523,8 @@ class HTTPRequest implements ArrayAccess $pattern = $matches[2]; } - // Special case for the root URL controller - if (!$pattern) { + // Special case for the root URL controller (designated as an empty string, or a slash) + if (!$pattern || $pattern === '/') { return ($this->dirParts == array()) ? array('Matched' => true) : false; } diff --git a/tests/php/Control/ControllerTest.php b/tests/php/Control/ControllerTest.php index a38648bc5..5e5ea8ea5 100644 --- a/tests/php/Control/ControllerTest.php +++ b/tests/php/Control/ControllerTest.php @@ -14,6 +14,7 @@ use SilverStripe\Control\Tests\ControllerTest\IndexSecuredController; use SilverStripe\Control\Tests\ControllerTest\SubController; use SilverStripe\Control\Tests\ControllerTest\TestController; use SilverStripe\Control\Tests\ControllerTest\UnsecuredController; +use SilverStripe\Control\Tests\RequestHandlingTest\HTTPMethodTestController; use SilverStripe\Dev\FunctionalTest; use SilverStripe\Security\Member; use SilverStripe\View\SSViewer; @@ -34,6 +35,7 @@ class ControllerTest extends FunctionalTest ContainerController::class, HasAction::class, HasAction_Unsecured::class, + HTTPMethodTestController::class, IndexSecuredController::class, SubController::class, TestController::class, @@ -493,4 +495,15 @@ class ControllerTest extends FunctionalTest $response = $this->get('ContainerController/subcontroller/substring/subvieweraction'); $this->assertEquals('Hope this works', $response->getBody()); } + + public function testSpecificHTTPMethods() + { + // 'GET /' + $response = $this->get('HTTPMethodTestController'); + $this->assertEquals('Routed to getRoot', $response->getBody()); + + // 'POST ' (legacy method of specifying root route) + $response = $this->post('HTTPMethodTestController', ['dummy' => 'example']); + $this->assertEquals('Routed to postLegacyRoot', $response->getBody()); + } } diff --git a/tests/php/Control/RequestHandlingTest/HTTPMethodTestController.php b/tests/php/Control/RequestHandlingTest/HTTPMethodTestController.php new file mode 100644 index 000000000..beb211e21 --- /dev/null +++ b/tests/php/Control/RequestHandlingTest/HTTPMethodTestController.php @@ -0,0 +1,32 @@ + 'getRoot', + 'POST ' => 'postLegacyRoot', + ]; + + private static $allowed_actions = [ + 'getRoot', + 'postLegacyRoot', + ]; + + public function getRoot(HTTPRequest $request) + { + return "Routed to getRoot"; + } + + public function postLegacyRoot(HTTPRequest $request) + { + return "Routed to postLegacyRoot"; + } +} diff --git a/tests/php/Control/RequestHandlingTest/TestFormHandler.php b/tests/php/Control/RequestHandlingTest/TestFormHandler.php index eae42fc8f..9ee8c41e7 100644 --- a/tests/php/Control/RequestHandlingTest/TestFormHandler.php +++ b/tests/php/Control/RequestHandlingTest/TestFormHandler.php @@ -13,8 +13,8 @@ class TestFormHandler extends FormRequestHandler { private static $url_handlers = array( 'fields/$FieldName' => 'handleField', - "POST " => "handleSubmission", - "GET " => "handleGet", + "POST /" => "handleSubmission", + "GET /" => "handleGet", ); // These are a different case from those in url_handlers to confirm that it's all case-insensitive