Routing

Il Router si occupa di tutto ciò che riguarda gli indirizzi URL, così non dovrai più pensarci tu. Vedremo:

  • come impostare il router affinché gli URL siano come desiderato
  • parleremo di SEO e redirect
  • e mostreremo come scrivere un router personalizzato

URL più umani (o anche cool o pretty URL) sono più usabili, memorizzabili e contribuiscono positivamente al SEO. Nette ci pensa e va incontro pienamente agli sviluppatori. Puoi progettare per la tua applicazione esattamente la struttura degli indirizzi URL che desideri. Puoi progettarla persino quando l'applicazione è già finita, perché si può fare senza interventi nel codice o nei template. Si definisce infatti in modo elegante in un unico posto, nel router, e non è quindi disseminata sotto forma di annotazioni in tutti i presenter.

Il router in Nette è eccezionale perché è bidirezionale. Sa sia decodificare gli URL nella richiesta HTTP, sia creare i link. Svolge quindi un ruolo fondamentale in Nette Application, perché decide quale presenter e azione eseguirà la richiesta corrente, ma viene anche utilizzato per la generazione di URL nel template, ecc.

Tuttavia, il router non è limitato solo a questo utilizzo, puoi usarlo in applicazioni dove i presenter non vengono affatto utilizzati, per API REST, ecc. Maggiori informazioni nella sezione Utilizzo indipendente.

Collezione di route

Il modo più piacevole per definire la forma degli indirizzi URL nell'applicazione è offerto dalla classe Nette\Application\Routers\RouteList. La definizione è costituita da un elenco delle cosiddette route, cioè maschere di indirizzi URL e presenter e azioni associati ad esse tramite una semplice API. Non è necessario dare un nome alle route.

$router = new Nette\Application\Routers\RouteList;
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('article/<id>', 'Article:view');
// ...

L'esempio dice che se apriamo https://domain.com/rss.xml nel browser, verrà visualizzato il presenter Feed con l'azione rss, se https://domain.com/article/12, verrà visualizzato il presenter Article con l'azione view, ecc. In caso di mancata corrispondenza di una route adatta, Nette Application reagisce lanciando un'eccezione BadRequestException, che viene mostrata all'utente come una pagina di errore 404 Not Found.

Ordine delle route

L'ordine in cui sono elencate le singole route è assolutamente cruciale, perché vengono valutate sequenzialmente dall'alto verso il basso. Vale la regola che dichiariamo le route dalle specifiche alle generali:

// SBAGLIATO: 'rss.xml' viene catturato dalla prima route e interpreta questa stringa come <slug>
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');

// GIUSTO
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');

Le route vengono valutate dall'alto verso il basso anche durante la generazione dei link:

// SBAGLIATO: il link a 'Feed:rss' genera come 'admin/feed/rss'
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');

// GIUSTO
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');

Non ti nasconderemo che la corretta composizione delle route richiede una certa abilità. Prima di padroneggiarla, ti sarà utile il pannello di routing.

Maschera e parametri

La maschera descrive il percorso relativo dalla directory principale del sito web. La maschera più semplice è un URL statico:

$router->addRoute('products', 'Products:default');

Spesso le maschere contengono i cosiddetti parametri. Questi sono indicati tra parentesi angolari (es. <year>) e vengono passati al presenter di destinazione, ad esempio al metodo renderShow(int $year) o al parametro persistente $year:

$router->addRoute('chronicle/<year>', 'History:show');

L'esempio dice che se apriamo https://example.com/chronicle/2020 nel browser, verrà visualizzato il presenter History con l'azione show e il parametro year: 2020.

Possiamo specificare un valore predefinito per i parametri direttamente nella maschera, rendendoli così opzionali:

$router->addRoute('chronicle/<year=2020>', 'History:show');

La route accetterà ora anche l'URL https://example.com/chronicle/, che visualizzerà nuovamente History:show con il parametro year: 2020.

Un parametro può ovviamente essere anche il nome del presenter e dell'azione. Ad esempio così:

$router->addRoute('<presenter>/<action>', 'Home:default');

La route specificata accetta ad es. URL nella forma /article/edit o anche /catalog/list e li interpreta come presenter e azioni Article:edit e Catalog:list.

Allo stesso tempo, assegna ai parametri presenter e action i valori predefiniti Home e default e sono quindi anche opzionali. Quindi la route accetta anche URL nella forma /article e la interpreta come Article:default. O viceversa, un link a Product:default genererà il percorso /product, un link al predefinito Home:default il percorso /.

La maschera può descrivere non solo il percorso relativo dalla directory principale del sito web, ma anche il percorso assoluto, se inizia con uno slash, o persino l'intero URL assoluto, se inizia con due slash:

// relativamente alla document root
$router->addRoute('<presenter>/<action>', /* ... */);

// percorso assoluto (relativo al dominio)
$router->addRoute('/<presenter>/<action>', /* ... */);

// URL assoluto incluso il dominio (relativo allo schema)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);

// URL assoluto incluso lo schema
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);

Espressioni di validazione

Per ogni parametro è possibile stabilire una condizione di validazione tramite un'espressione regolare. Ad esempio, per il parametro id specifichiamo che può assumere solo cifre tramite l'espressione regolare \d+:

$router->addRoute('<presenter>/<action>[/<id \d+>]', /* ... */);

L'espressione regolare predefinita per tutti i parametri è [^/]+, cioè tutto tranne lo slash. Se un parametro deve accettare anche gli slash, specifichiamo l'espressione .+:

// accetta https://example.com/a/b/c, path sarà 'a/b/c'
$router->addRoute('<path .+>', /* ... */);

Sequenze opzionali

Nella maschera è possibile contrassegnare parti opzionali tramite parentesi quadre. Qualsiasi parte della maschera può essere opzionale, possono esserci anche parametri al suo interno:

$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);

// Accetta percorsi:
//    /cs/download  => lang => cs, name => download
//    /download     => lang => null, name => download

Quando un parametro fa parte di una sequenza opzionale, diventa ovviamente anche opzionale. Se non ha un valore predefinito specificato, sarà null.

Le parti opzionali possono essere anche nel dominio:

$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);

Le sequenze possono essere nidificate e combinate liberamente:

$router->addRoute(
	'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
	'Home:default',
);

// Accetta percorsi:
// 	/cs/hello
// 	/en-us/hello
// 	/hello
// 	/hello/page-12

Durante la generazione dell'URL, si cerca la variante più corta, quindi tutto ciò che può essere omesso viene omesso. Per questo, ad esempio, la route index[.html] genera il percorso /index. È possibile invertire il comportamento specificando un punto esclamativo dopo la parentesi quadra sinistra:

// accetta /hello e /hello.html, genera /hello
$router->addRoute('<name>[.html]', /* ... */);

// accetta /hello e /hello.html, genera /hello.html
$router->addRoute('<name>[!.html]', /* ... */);

I parametri opzionali (cioè i parametri con un valore predefinito) senza parentesi quadre si comportano essenzialmente come se fossero racchiusi tra parentesi nel modo seguente:

$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);

// corrisponde a questo:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);

Se volessimo influenzare il comportamento dello slash finale, in modo che ad esempio invece di /home/ venga generato solo /home, si può ottenere così:

$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);

Caratteri jolly

Nella maschera del percorso assoluto possiamo utilizzare i seguenti caratteri jolly ed evitare così, ad esempio, la necessità di scrivere nella maschera il dominio, che può differire nell'ambiente di sviluppo e produzione:

  • %tld% = top level domain, es. comorg
  • %sld% = second level domain, es. example
  • %domain% = dominio senza sottodomini, es. example.com
  • %host% = intero host, es. www.example.com
  • %basePath% = percorso alla directory principale
$router->addRoute('//www.%domain%/%basePath%/<presenter>/<action>', /* ... */);
$router->addRoute('//www.%sld%.%tld%/%basePath%/<presenter>/<action', /* ... */);

Notazione estesa

La destinazione della route, solitamente scritta nella forma Presenter:action, può anche essere scritta utilizzando un array che definisce i singoli parametri e i loro valori predefiniti:

$router->addRoute('<presenter>/<action>[/<id \d+>]', [
	'presenter' => 'Home',
	'action' => 'default',
]);

Per una specifica più dettagliata, è possibile utilizzare una forma ancora più estesa, dove oltre ai valori predefiniti possiamo impostare altre proprietà dei parametri, come ad esempio l'espressione regolare di validazione (vedi parametro id):

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>[/<id>]', [
	'presenter' => [
		Route::Value => 'Home',
	],
	'action' => [
		Route::Value => 'default',
	],
	'id' => [
		Route::Pattern => '\d+',
	],
]);

È importante notare che se i parametri definiti nell'array non sono specificati nella maschera del percorso, i loro valori non possono essere modificati, nemmeno tramite i parametri query specificati dopo il punto interrogativo nell'URL.

Filtri e traduzioni

Scriviamo il codice sorgente dell'applicazione in inglese, ma se il sito web deve avere URL in italiano, allora un semplice routing del tipo:

$router->addRoute('<presenter>/<action>', 'Home:default');

genererà URL in inglese, come /product/123 o /cart. Se vogliamo che i presenter e le azioni nell'URL siano rappresentati da parole italiane (es. /prodotto/123 o /carrello), possiamo utilizzare un dizionario di traduzione. Per la sua scrittura abbiamo già bisogno della variante “più verbosa” del secondo parametro:

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterTable => [
			// stringa nell'URL => presenter
			'prodotto' => 'Product',
			'carrello' => 'Cart',
			'catalogo' => 'Catalog',
		],
	],
	'action' => [
		Route::Value => 'default',
		Route::FilterTable => [
			'elenco' => 'list',
		],
	],
]);

Più chiavi del dizionario di traduzione possono portare allo stesso presenter. In questo modo si creano diversi alias per esso. La variante canonica (cioè quella che sarà nell'URL generato) è considerata l'ultima chiave.

La tabella di traduzione può essere utilizzata in questo modo per qualsiasi parametro. Se la traduzione non esiste, viene preso il valore originale. Possiamo cambiare questo comportamento aggiungendo Route::FilterStrict => true e la route rifiuterà quindi l'URL se il valore non è nel dizionario.

Oltre al dizionario di traduzione sotto forma di array, è possibile implementare anche funzioni di traduzione personalizzate.

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>/<id>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterIn => function (string $s): string { /* ... */ },
		Route::FilterOut => function (string $s): string { /* ... */ },
	],
	'action' => 'default',
	'id' => null,
]);

La funzione Route::FilterIn converte tra il parametro nell'URL e la stringa, che viene poi passata al presenter, la funzione FilterOut assicura la conversione nella direzione opposta.

I parametri presenter, action e module hanno già filtri predefiniti che convertono tra lo stile PascalCase o camelCase e kebab-case utilizzato nell'URL. Il valore predefinito dei parametri viene scritto già nella forma trasformata, quindi ad esempio nel caso del presenter scriviamo <presenter=ProductEdit>, non <presenter=product-edit>.

Filtri generali

Oltre ai filtri destinati a parametri specifici, possiamo definire anche filtri generali, che ricevono un array associativo di tutti i parametri, che possono modificare in qualsiasi modo e poi restituirli. I filtri generali li definiamo sotto la chiave null.

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => 'Home',
	'action' => 'default',
	null => [
		Route::FilterIn => function (array $params): array { /* ... */ },
		Route::FilterOut => function (array $params): array { /* ... */ },
	],
]);

I filtri generali danno la possibilità di modificare il comportamento della route in modo assolutamente qualsiasi. Possiamo usarli ad esempio per modificare i parametri in base ad altri parametri. Ad esempio, la traduzione di <presenter> e <action> in base al valore corrente del parametro <lang>.

Se un parametro ha definito un filtro personalizzato e contemporaneamente esiste un filtro generale, viene eseguito il FilterIn personalizzato prima di quello generale e viceversa il FilterOut generale prima di quello personalizzato. Quindi all'interno del filtro generale i valori dei parametri presenter o action sono scritti nello stile PascalCase o camelCase.

Sensi unici OneWay

Le route a senso unico vengono utilizzate per mantenere la funzionalità dei vecchi URL, che l'applicazione non genera più, ma accetta ancora. Le contrassegniamo con il flag OneWay:

// vecchio URL /product-info?id=123
$router->addRoute('product-info', 'Product:detail', $router::ONE_WAY);
// nuovo URL /product/123
$router->addRoute('product/<id>', 'Product:detail');

Accedendo al vecchio URL, il presenter reindirizza automaticamente al nuovo URL, così i motori di ricerca non indicizzeranno due volte queste pagine (vedi SEO e canonizzazione).

Routing dinamico con callback

Il routing dinamico con callback ti consente di assegnare direttamente alle route funzioni (callback) che vengono eseguite quando viene visitato il percorso dato. Questa funzionalità flessibile ti consente di creare rapidamente ed efficacemente vari endpoint per la tua applicazione:

$router->addRoute('test', function () {
	echo 'sei all\'indirizzo /test';
});

Puoi anche definire parametri nella maschera, che verranno automaticamente passati al tuo callback:

$router->addRoute('<lang cs|en>', function (string $lang) {
	echo match ($lang) {
		'cs' => 'Vítejte na české verzi našeho webu!', // Welcome to the Czech version of our website!
		'en' => 'Welcome to the English version of our website!',
	};
});

Moduli

Se abbiamo più route che rientrano in un modulo comune, utilizziamo withModule():

$router = new RouteList;
$router->withModule('Forum') // le seguenti route fanno parte del modulo Forum
	->addRoute('rss', 'Feed:rss') // il presenter sarà Forum:Feed
	->addRoute('<presenter>/<action>')

	->withModule('Admin') // le seguenti route fanno parte del modulo Forum:Admin
		->addRoute('sign:in', 'Sign:in');

Un'alternativa è l'uso del parametro module:

// L'URL manage/dashboard/default si mappa sul presenter Admin:Dashboard
$router->addRoute('manage/<presenter>/<action>', [
	'module' => 'Admin',
]);

Sottodomini

Possiamo suddividere le collezioni di route per sottodomini:

$router = new RouteList;
$router->withDomain('example.com')
	->addRoute('rss', 'Feed:rss')
	->addRoute('<presenter>/<action>');

Nel nome del dominio è possibile utilizzare anche Caratteri jolly:

$router = new RouteList;
$router->withDomain('example.%tld%')
	// ...

Prefisso del percorso

Possiamo suddividere le collezioni di route per percorso nell'URL:

$router = new RouteList;
$router->withPath('eshop')
	->addRoute('rss', 'Feed:rss') // cattura l'URL /eshop/rss
	->addRoute('<presenter>/<action>'); // cattura l'URL /eshop/<presenter>/<action>

Combinazioni

Le suddivisioni sopra menzionate possono essere combinate tra loro:

$router = (new RouteList)
	->withDomain('admin.example.com')
		->withModule('Admin')
			->addRoute(/* ... */)
			->addRoute(/* ... */)
		->end()
		->withModule('Images')
			->addRoute(/* ... */)
		->end()
	->end()
	->withDomain('example.com')
		->withPath('export')
			->addRoute(/* ... */)
			// ...

Parametri query

Le maschere possono anche contenere parametri query (parametri dopo il punto interrogativo nell'URL). A questi non è possibile definire un'espressione di validazione, ma è possibile cambiare il nome con cui vengono passati al presenter:

// il parametro query 'cat' vogliamo usarlo nell'applicazione con il nome 'categoryId'
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);

Parametri Foo

Ora andiamo più a fondo. I parametri Foo sono essenzialmente parametri senza nome che consentono di abbinare un'espressione regolare. Un esempio è una route che accetta /index, /index.html, /index.htm e /index.php:

$router->addRoute('index<? \.html?|\.php|>', /* ... */);

È anche possibile definire esplicitamente la stringa che verrà utilizzata durante la generazione dell'URL. La stringa deve essere posizionata direttamente dopo il punto interrogativo. La seguente route è simile alla precedente, ma genera /index.html invece di /index, perché la stringa .html è impostata come valore di generazione:

$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);

Integrazione nell'applicazione

Per integrare il router creato nell'applicazione, dobbiamo informarne il container DI. Il modo più semplice è preparare una factory che produca l'oggetto router e comunicare nella configurazione del container che deve usarla. Supponiamo di scrivere a tale scopo il metodo App\Core\RouterFactory::createRouter():

namespace App\Core;

use Nette\Application\Routers\RouteList;

class RouterFactory
{
	public static function createRouter(): RouteList
	{
		$router = new RouteList;
		$router->addRoute(/* ... */);
		return $router;
	}
}

Nella configurazione scriveremo quindi:

services:
	- App\Core\RouterFactory::createRouter

Qualsiasi dipendenza, ad esempio dal database ecc., viene passata al metodo factory come suoi parametri tramite autowiring:

public static function createRouter(Nette\Database\Connection $db): RouteList
{
	// ...
}

SimpleRouter

Un router molto più semplice della collezione di route è SimpleRouter. Lo useremo quando non abbiamo particolari esigenze sulla forma dell'URL, se non è disponibile mod_rewrite (o le sue alternative) o se per ora non vogliamo occuparci di URL leggibili.

Genera indirizzi approssimativamente in questa forma:

http://example.com/?presenter=Product&action=detail&id=123

Il parametro del costruttore di SimpleRouter è il presenter & azione predefinito, a cui si deve puntare se apriamo la pagina senza parametri, ad es. http://example.com/.

// il presenter predefinito sarà 'Home' e l'azione 'default'
$router = new Nette\Application\Routers\SimpleRouter('Home:default');

Consigliamo di definire SimpleRouter direttamente nella configurazione:

services:
	- Nette\Application\Routers\SimpleRouter('Home:default')

SEO e canonizzazione

Il framework contribuisce al SEO (ottimizzazione della reperibilità su Internet) impedendo la duplicazione di contenuti su URL diversi. Se a una determinata destinazione portano più indirizzi, ad es. /index e /index.html, il framework determina il primo come primario (canonico) e reindirizza gli altri ad esso tramite il codice HTTP 301. Grazie a ciò, i motori di ricerca non indicizzano le pagine due volte e non diluiscono il loro page rank.

Questo processo si chiama canonizzazione. L'URL canonico è quello generato dal router, cioè la prima route corrispondente nella collezione senza il flag OneWay. Pertanto, nella collezione elenchiamo le route primarie per prime.

La canonizzazione viene eseguita dal presenter, maggiori informazioni nel capitolo canonizzazione.

HTTPS

Per poter utilizzare il protocollo HTTPS, è necessario abilitarlo sull'hosting e configurare correttamente il server.

Il redirect dell'intero sito a HTTPS deve essere impostato a livello di server, ad esempio tramite il file .htaccess nella directory principale della nostra applicazione, e con il codice HTTP 301. L'impostazione può variare a seconda dell'hosting e assomiglia circa a questo:

<IfModule mod_rewrite.c>
	RewriteEngine On
	...
	RewriteCond %{HTTPS} off
	RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
	...
</IfModule>

Il router genera URL con lo stesso protocollo con cui è stata caricata la pagina, quindi non è necessario impostare nient'altro.

Se però eccezionalmente abbiamo bisogno che diverse route vengano eseguite sotto protocolli diversi, lo specifichiamo nella maschera della route:

// Genererà un indirizzo con HTTP
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);

// Genererà un indirizzo con HTTPs
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);

Debug del router

Il pannello di routing visualizzato nella Tracy Bar è un aiuto utile che mostra l'elenco delle route e anche i parametri che il router ha ottenuto dall'URL.

La barra verde con il simbolo ✓ rappresenta la route che ha elaborato l'URL corrente, con il colore blu e il simbolo ≈ sono contrassegnate le route che avrebbero elaborato anche l'URL se la verde non le avesse precedute. Vediamo inoltre il presenter & azione corrente.

Allo stesso tempo, se si verifica un redirect inaspettato a causa della canonizzazione, è utile guardare il pannello nella barra redirect, dove scoprirete come il router ha originariamente compreso l'URL e perché ha reindirizzato.

Durante il debug del router, consigliamo di aprire gli Strumenti per sviluppatori nel browser (Ctrl+Shift+I o Cmd+Option+I) e nel pannello Network disattivare la cache, in modo che non vi vengano salvati i redirect.

Prestazioni

Il numero di route influisce sulla velocità del router. Il loro numero non dovrebbe assolutamente superare alcune decine. Se il tuo sito web ha una struttura URL troppo complicata, puoi scrivere un Router personalizzato su misura.

Se il router non ha dipendenze, ad esempio dal database, e la sua factory non accetta argomenti, possiamo serializzare la sua forma compilata direttamente nel container DI e accelerare così leggermente l'applicazione.

routing:
	cache: true

Router personalizzato

Le righe seguenti sono destinate a utenti molto avanzati. Puoi creare un tuo router personalizzato e integrarlo in modo del tutto naturale nella collezione di route. Il router è un'implementazione dell'interfaccia Nette\Routing\Router con due metodi:

use Nette\Http\IRequest as HttpRequest;
use Nette\Http\UrlScript;

class MyRouter implements Nette\Routing\Router
{
	public function match(HttpRequest $httpRequest): ?array
	{
		// ...
	}

	public function constructUrl(array $params, UrlScript $refUrl): ?string
	{
		// ...
	}
}

Il metodo match elabora la richiesta corrente $httpRequest, dalla quale è possibile ottenere non solo l'URL, ma anche gli header, ecc., in un array contenente il nome del presenter e i suoi parametri. Se non sa elaborare la richiesta, restituisce null. Durante l'elaborazione della richiesta, dobbiamo restituire almeno il presenter e l'azione. Il nome del presenter è completo e contiene anche eventuali moduli:

[
	'presenter' => 'Front:Home',
	'action' => 'default',
]

Il metodo constructUrl al contrario costruisce dall'array di parametri l'URL assoluto risultante. A tal fine può utilizzare le informazioni dal parametro $refUrl, che è l'URL corrente.

Lo aggiungi alla collezione di route usando add():

$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...

Utilizzo indipendente

Per utilizzo indipendente intendiamo l'utilizzo delle capacità del router in un'applicazione che non utilizza Nette Application e i presenter. Vale per esso quasi tutto ciò che abbiamo mostrato in questo capitolo, con queste differenze:

Quindi di nuovo creiamo un metodo che ci costruisca il router, ad es.:

namespace App\Core;

use Nette\Routing\RouteList;

class RouterFactory
{
	public static function createRouter(): RouteList
	{
		$router = new RouteList;
		$router->addRoute('rss.xml', [
			'controller' => 'RssFeedController',
		]);
		$router->addRoute('article/<id \d+>', [
			'controller' => 'ArticleController',
		]);
		// ...
		return $router;
	}
}

Se usi un container DI, cosa che consigliamo, aggiungiamo di nuovo il metodo alla configurazione e poi otteniamo il router insieme alla richiesta HTTP dal container:

$router = $container->getByType(Nette\Routing\Router::class);
$httpRequest = $container->getByType(Nette\Http\IRequest::class);

Oppure creiamo direttamente gli oggetti:

$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();

Ora resta solo da mettere al lavoro il router:

$params = $router->match($httpRequest);
if ($params === null) {
	// non è stata trovata una route corrispondente, inviamo errore 404
	exit;
}

// elaboriamo i parametri ottenuti
$controller = $params['controller'];
// ...

E viceversa usiamo il router per costruire un link:

$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());