The RouterInterface Contract
Every router in Magento 2 must implement the Magento\Framework\App\RouterInterface. This interface defines a single method that is the contract for request matching.
<?php
namespace Magento\Framework\App\Router;
interface RouterInterface
{
/**
* Match a request to a router action
*
* @param \Magento\Framework\App\RequestInterface $request
* @return \Magento\Framework\App\Action\ActionInterface|null
*/
public function match(\Magento\Framework\App\RequestInterface $request);
}
The match() method receives the current request object and must return:
- An
ActionInterfaceinstance if the router can handle the request nullif it cannot, allowing the next router to try
Router classes are injected into the FrontController as an array. The order in the array determines execution priority. Magento processes routers sequentially and stops at the first match.
You can inspect the current router list using the ObjectManager or via DI compilation output. The routers are registered in Magento/Framework/App/FrontController.php through constructor injection.
StandardRouter Deep Dive
The StandardRouter is the primary router that handles module/controller/action routing based on routes.xml configurations.
// Simplified StandardRouter matching logic
public function match(\Magento\Framework\App\RequestInterface $request)
{
$pathInfo = trim($request->getPathInfo(), '/');
$parts = explode('/', $pathInfo);
if (empty($parts[0])) {
return null;
}
// Try to find matching route by front name
$frontName = $parts[0];
$routeConfig = $this->routeConfig->getRouteByFrontName($frontName);
if ($routeConfig === null) {
return null;
}
// Set route information on request
$request->setRouteName($routeConfig->getRouteId());
$request->setModuleName($routeConfig->getModuleName());
// Parse controller and action from remaining URL segments
$controllerName = $parts[1] ?? 'index';
$actionName = $parts[2] ?? 'index';
$request->setControllerName($controllerName);
$request->setActionName($actionName);
return $this->actionFactory->create(
\Magento\Framework\App\Action\Forward::class
);
}
The StandardRouter relies on Magento\Framework\App\Route\ConfigInterface to load all route definitions from compiled configuration. Routes are loaded from etc/{area}/routes.xml across all enabled modules.
Controller classes must follow the naming convention:
App/Code/Vendor/Module/Controller/{ControllerName}/{ActionName}.php
The controller class must extend Magento\Framework\App\Action\Action (or Magento\Framework\App\Action\AbstractAction) and implement an execute() method that returns a ResultInterface.
UrlRewriteRouter and SEO URLs
The UrlRewriteRouter handles SEO-friendly URL rewrites. It queries the url_rewrite database table to find matching URL rules.
// UrlRewriteRouter match logic (simplified)
public function match(\Magento\Framework\App\RequestInterface $request)
{
$requestPath = $request->getPathInfo();
// Look up URL rewrite rule
$rewrite = $this->urlFinder->findOneByData([
'request_path' => $requestPath,
'store_id' => $request->getStoreId(),
]);
if ($rewrite === null) {
return null;
}
// Set target route information
$request->setRouteName($rewrite->getRouteName());
$request->setModuleName($rewrite->getModuleName());
$request->setControllerName($rewrite->getControllerName());
$request->setActionName($rewrite->getActionName());
// Store rewrite metadata
$request->setAlias(
\Magento\Framework\Url\Router\RewriteRequestAlias::CATALOG_REWRITE,
$rewrite->getTargetPath()
);
return $this->actionFactory->create(
\Magento\Framework\App\Action\Forward::class
);
}
URL rewrites are generated automatically for products, categories, and CMS pages. You can create custom rewrites through the admin panel or programmatically:
$rewrite = $this->rewriteFactory->create();
$rewrite->setStoreId(1)
->setRequestPath('custom-url')
->setTargetPath('catalog/product/view/id/5')
->setEntityType('product')
->setEntityId(5);
$this->rewriteResource->save($rewrite);
Custom Routers and Priority
Creating a custom router involves implementing RouterInterface and registering it via dependency injection.
Complete custom router example:
<?php
namespace Vendor\Module\Controller\Router;
use Magento\Framework\App\ActionFactory;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\App\RouterInterface;
class LanguageRouter implements RouterInterface
{
private $actionFactory;
private $storeManager;
public function __construct(
ActionFactory $actionFactory,
\Magento\Store\Model\StoreManagerInterface $storeManager
) {
$this->actionFactory = $actionFactory;
$this->storeManager = $storeManager;
}
public function match(RequestInterface $request)
{
$pathInfo = $request->getPathInfo();
// Match pattern: /en/product/... or /fr/product/...
if (preg_match('#^/(en|fr)/(.+)$#', $pathInfo, $matches)) {
$langCode = $matches[1];
$request->setPathInfo('/' . $matches[2]);
// Set store based on language
$store = $this->storeManager->getStore($langCode);
$request->setStoreId($store->getId());
return $this->actionFactory->create(
\Magento\Framework\App\Action\Forward::class
);
}
return null;
}
}
Register with priority via di.xml:
<config>
<type name="Magento\Framework\App\FrontController">
<arguments>
<argument name="routers" xsi:type="array">
<item name="language" xsi:type="object" sortOrder="10">
Vendor\Module\Controller\Router\LanguageRouter
</item>
</argument>
</arguments>
</type>
</config>
The sortOrder attribute controls router priority. Lower values execute first. The StandardRouter typically has sortOrder 30, so custom routers with lower sortOrder run before it. This lets you intercept requests before standard routing occurs.
Debugging Router Issues
When routing doesn't work as expected, there are several debugging approaches.
Enable route logging in development:
// Add to app/bootstrap.php for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
Check compiled route configuration:
php bin/magento setup:di:compile
# Check generated/metadata/global.php for route definitions
Verify your routes.xml is being loaded:
php bin/magento module:status Vendor_Module
# Ensure module is enabled
Inspect the router chain at runtime:
// In a custom module or script
$objectManager = \Magento\Framework\App\Bootstrap::create(
BP,
$_SERVER
)->getobjectManager();
$frontController = $objectManager->get(
\Magento\Framework\App\FrontControllerInterface::class
);
// Check registered routers via reflection
$reflection = new \ReflectionClass($frontController);
$routersProperty = $reflection->getProperty('routers');
$routersProperty->setAccessible(true);
$routers = $routersProperty->getValue($frontController);
echo "Registered routers:\n";
foreach ($routers as $name => $router) {
echo "- $name: " . get_class($router) . "\n";
}
Common routing issues:
- Module not enabled (check
app/etc/config.php) - Routes.xml has syntax errors
- Controller class doesn't exist or doesn't extend Action
- Controller class namespace doesn't follow convention
- Duplicate front names causing route conflicts
- Store ID not set for multi-store setups
Quiz
1. What must a custom router implement?
2. How do you control router execution order?
3. What happens when UrlRewriteRouter finds a match?
Flashcards
Question
What method must RouterInterface implement?
Click to reveal answer
Answer
match(RequestInterface $request) returning ActionInterface or null
Question
Which router handles module/controller/action routing?
Click to reveal answer
Answer
StandardRouter
Question
What database table stores URL rewrites?
Click to reveal answer
Answer
url_rewrite
Question
How do you register a custom router?
Click to reveal answer
Answer
Via di.xml, adding to Magento\Framework\App\FrontController routers argument
Revision Notes
Key Takeaways
- 1. RouterInterface requires a match() method returning ActionInterface or null
- 2. StandardRouter resolves controllers from routes.xml configurations
- 3. UrlRewriteRouter queries the url_rewrite database table for SEO URLs
- 4. Custom routers are registered via di.xml with optional sortOrder
- 5. Lower sortOrder values execute first in the router chain
- 6. Router chain stops at the first match (chain of responsibility pattern)
Interview Tips
- • Explain how you would create a language-prefix router for multi-language stores
- • Discuss the difference between forwarding and redirecting in router context
- • Describe how to debug routing issues in Magento 2
- • Explain why Magento uses a chain of routers instead of a single router
- • Know the standard router priority order
Cheat Sheet
Router Classes Cheat Sheet
Interface: Magento\Framework\App\RouterInterface
Method: match(RequestInterface $request) : ActionInterface|null
Built-in Routers:
- StandardRouter (sortOrder ~30) - module/controller/action
- UrlRewriteRouter - database URL rewrites
- BaseUrlRouter - store switching
- DefaultRouter - CMS pages, noroute
Custom Router Registration:
<type name="Magento\Framework\App\FrontController">
<arguments>
<argument name="routers" xsi:type="array">
<item name="custom" xsi:type="object" sortOrder="10">
Vendor\Module\Controller\Router\CustomRouter
</item>
</argument>
</arguments>
</type>