Roteiro

O roteador é responsável por tudo sobre URLs, para que você não tenha mais que pensar nelas. Nós vamos mostrar:

  • como configurar o roteador para que as URLs pareçam como você quer
  • algumas notas sobre o redirecionamento de SEO
  • e nós lhe mostraremos como escrever seu próprio roteador

URLs mais humanas (ou mais legais ou bonitas) são mais utilizáveis, mais memoráveis e contribuem positivamente para SEO. Nette tem isto em mente e atende plenamente aos desejos dos desenvolvedores. Você pode projetar sua estrutura de URLs para sua aplicação exatamente do jeito que você quiser. Você pode até projetá-la depois que o aplicativo estiver pronto, pois pode ser feita sem qualquer mudança de código ou modelo. Ela é definida de forma elegante em um único lugar, no roteador, e não está dispersa na forma de anotações em todos os apresentadores.

O roteador em Nette é especial porque é bidirecional, ele pode tanto decodificar URLs de solicitação HTTP quanto criar links. Portanto, ele desempenha um papel vital na Aplicação Nette, pois decide qual apresentador e ação executará a solicitação atual, e também é utilizado para geração de URLs no modelo, etc.

Entretanto, o roteador não está limitado a este uso, você pode usá-lo em aplicações onde os apresentadores não são usados, para APIs REST, etc. Mais na seção uso separado.

Coleta de rotas

A maneira mais agradável de definir os endereços URL na aplicação é através da classe Nette\Application\Routers\RouteList. A definição consiste em uma lista das chamadas rotas, ou seja, máscaras de endereços URL e seus apresentadores associados e ações utilizando uma API simples. Não é necessário nomear as rotas.

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

O exemplo diz que se abrirmos https://any-domain.com/rss.xml com a ação rss será exibido, se https://domain.com/article/12 com a ação view será exibido, etc. Se nenhum caminho adequado for encontrado, a Aplicação Nette responde lançando uma exceção BadRequestException, que aparece ao usuário como uma página de erro 404 Não encontrado.

Ordem das Rotas

A ordem na qual as rotas são listadas é muito importante porque são avaliadas seqüencialmente de cima para baixo. A regra é que declaramos as rotas **de específicas a gerais***:

// ERRADO: 'rss.xml' corresponde à primeira rota e entende isto mal como <slug>
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');

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

As rotas também são avaliadas de cima para baixo quando os links são gerados:

// ERRADO: gera um link para 'Feed:rss' como 'admin/feed/rss'.
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');

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

Não vamos esconder de você que é preciso alguma habilidade para construir uma lista corretamente. Até que você entre nela, o painel de roteamento será uma ferramenta útil.

Máscara e Parâmetros

A máscara descreve o caminho relativo com base na raiz do local. A máscara mais simples é uma URL estática:

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

Muitas vezes as máscaras contêm os chamados **parâmetros***. Elas estão entre parênteses angulares (por exemplo <year>) e são passadas ao apresentador alvo, por exemplo, ao método renderShow(int $year) ou ao parâmetro persistente $year:

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

O exemplo diz que se abrirmos https://any-domain.com/chronicle/2020 e a ação show com o parâmetro year: 2020 serão exibidos.

Podemos especificar um valor padrão para os parâmetros diretamente na máscara e assim ele se torna opcional:

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

A rota agora aceitará a URL https://any-domain.com/chronicle/ com o parâmetro year: 2020.

Naturalmente, o nome do apresentador e a ação também podem ser um parâmetro. Por exemplo, o nome do apresentador e a ação também podem ser um parâmetro:

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

Esta rota aceita, por exemplo, uma URL no formulário /article/edit resp. /catalog/list e as traduz para os apresentadores e ações Article:edit resp. Catalog:list.

Também dá aos parâmetros presenter e action valores padrãoHome e default e, portanto, são opcionais. Portanto, a rota também aceita uma URL /article e a traduz como Article:default. Ou vice versa, um link para Product:default gera um caminho /product, um link para o padrão Home:default gera um caminho /.

A máscara pode descrever não apenas o caminho relativo baseado na raiz do site, mas também o caminho absoluto quando ele começa com uma barra, ou mesmo todo o URL absoluto quando começa com duas barras:

// caminho relativo à raiz do documento de aplicação
$router->addRoute('<presenter>/<action>', /* ... */);

// caminho absoluto, relativo ao nome da hostname do servidor
$router->addRoute('/<presenter>/<action>', /* ... */);

// URL absoluto, incluindo o nome da hostname (mas esquema-relativo)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);

// URL absoluta, incluindo o esquema
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);

Expressões de validação

Uma condição de validação pode ser especificada para cada parâmetro usando expressão regular. Por exemplo, vamos definir id para ser apenas numérico, usando \d+ regexp:

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

A expressão regular padrão para todos os parâmetros é [^/]+ou seja, tudo, exceto a barra. Se um parâmetro deve corresponder a uma barra também, definimos a expressão regular para .+.

// aceita https://example.com/a/b/c, o caminho é 'a/b/c'.
$router->addRoute('<path .+>', /* ... */);

Seqüências Opcionais

Colchetes quadrados denotam partes opcionais da máscara. Qualquer parte da máscara pode ser definida como opcional, incluindo aquelas que contenham parâmetros:

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

// URLs aceitas:      Parâmetros:
//   /en/download        lang => en, name => download
//   /download           lang => null, name => download

Naturalmente, quando um parâmetro faz parte de uma seqüência opcional, ele também se torna opcional. Se não tiver um valor padrão, ele será nulo.

As seções opcionais também podem estar no domínio:

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

As seqüências podem ser livremente aninhadas e combinadas:

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

// URLs aceitas:
//   /en/hello
//   /en-us/hello
//   /hello
//   /hello/page-12

O gerador de URL tenta manter a URL o mais curta possível, de modo que o que pode ser omitido é omitido. Portanto, por exemplo, uma rota index[.html] gera um caminho /index. Você pode reverter este comportamento escrevendo um ponto de exclamação após o colchete à esquerda:

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

// aceita tanto /hello como /hello.html, gera /hello.html
$router->addRoute('<name>[!.html]', /* ... */);

Os parâmetros opcionais (ou seja, parâmetros com valor padrão) sem parênteses rectos comportam-se como se fossem embrulhados desta forma:

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

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

Para mudar a forma como a barra mais à direita é gerada, ou seja, ao invés de /home/, obtenha um /home, ajuste a rota desta forma:

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

Wildcards

Na máscara do caminho absoluto, podemos usar os seguintes wildcards para evitar, por exemplo, a necessidade de escrever um domínio para a máscara, que pode diferir no ambiente de desenvolvimento e produção:

  • %tld% = domínio de primeiro nível, por exemplo com ou org
  • %sld% = domínio de segundo nível, por exemplo example
  • %domain% = domínio sem subdomínios, p. ex. example.com
  • %host% = host inteiro, p.ex. www.example.com
  • %basePath% = caminho para o diretório raiz
$router->addRoute('//www.%domain%/%basePath%/<presenter>/<action>', /* ... */);
$router->addRoute('//www.%sld%.%tld%/%basePath%/<presenter>/<action', /* ... */);

Notação avançada

O segundo parâmetro da rota, que freqüentemente escrevemos no formato Presenter:action, é uma abreviação, que também podemos escrever na forma de um campo, onde indicamos diretamente os valores (padrão) dos parâmetros individuais:

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

Ou podemos usar este formulário, observando a reescrita da expressão regular de validação:

use Nette\Routing\Route;

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

Estes formatos mais faladores são úteis para adicionar outros metadados.

Filtros e Traduções

É uma boa prática escrever o código fonte em inglês, mas e se você precisar que seu website tenha o URL traduzido para outro idioma? Rotas simples, como por exemplo:

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

gerará URLs em inglês, tais como /product/123 ou /cart. Se quisermos ter apresentadores e ações na URL traduzidas para o Deutsch (por exemplo /produkt/123 ou /einkaufswagen), podemos usar um dicionário de tradução. Para adicioná-lo, já precisamos de uma variante “mais faladora” do segundo parâmetro:

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterTable => [
			// string na URL => apresentador
			'produkt' => 'Product',
			'einkaufswagen' => 'Cart',
			'katalog' => 'Catalog',
		],
	],
	'action' => [
		Route::Value => 'default',
		Route::FilterTable => [
			'liste' => 'list',
		],
	],
]);

As chaves de múltiplos dicionários podem ser usadas para o mesmo apresentador. Elas criarão vários pseudônimos para ele. A última chave é considerada como a variante canônica (ou seja, aquela que estará na URL gerada).

A tabela de tradução pode ser aplicada a qualquer parâmetro desta forma. Entretanto, se a tradução não existir, é tomado o valor original. Podemos mudar este comportamento adicionando Route::FilterStrict => true e a rota rejeitará a URL se o valor não estiver no dicionário.

Além do dicionário de tradução na forma de um array, é possível definir funções de tradução próprias:

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,
]);

A função Route::FilterIn converte entre o parâmetro na URL e a string, que é então passada para o apresentador, a função FilterOut assegura a conversão na direção oposta.

Os parâmetros presenter, action e module já possuem filtros predefinidos que convertem entre o PascalCase resp. estilo camelCase e a caixa kebab utilizada na URL. O valor padrão dos parâmetros já está escrito na forma transformada, assim, por exemplo, no caso de um apresentador, nós escrevemos <presenter=ProductEdit> em vez de <presenter=product-edit>.

Filtros gerais

Além dos filtros para parâmetros específicos, você também pode definir filtros gerais que recebem uma matriz associativa de todos os parâmetros que podem modificar de qualquer forma e depois retornar. Os filtros gerais são definidos sob a tecla 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 { /* ... */ },
	],
]);

Os filtros gerais lhe dão a capacidade de ajustar o comportamento da rota de absolutamente qualquer maneira. Podemos usá-los, por exemplo, para modificar parâmetros com base em outros parâmetros. Por exemplo, a tradução <presenter> e <action> com base no valor atual do parâmetro <lang>.

Se um parâmetro tem um filtro personalizado definido e um filtro geral existe ao mesmo tempo, o personalizado FilterIn é executado antes do geral e vice-versa geral FilterOut é executado antes do personalizado. Assim, dentro do filtro geral estão os valores dos parâmetros presenter resp. action escritos em estilo PascalCase resp. camelCase.

Bandeira OneWay

Rotas unidirecionais são usadas para preservar a funcionalidade de URLs antigas que a aplicação não gera mais, mas ainda assim aceita. Nós as sinalizamos com OneWay:

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

Ao acessar a antiga URL, o apresentador redireciona automaticamente para a nova URL para que os mecanismos de busca não indexem essas páginas duas vezes (ver SEO e canonização).

Roteamento dinâmico com retornos de chamada

O roteamento dinâmico com retornos de chamada permite atribuir diretamente funções (retornos de chamada) às rotas, que serão executadas quando o caminho especificado for visitado. Esse recurso flexível permite a criação rápida e eficiente de vários pontos de extremidade para o seu aplicativo:

$router->addRoute('test', function () {
	echo 'You are at the /test address';
});

Você também pode definir parâmetros na máscara, que serão automaticamente passados para o callback:

$router->addRoute('<lang cs|en>', function (string $lang) {
	echo match ($lang) {
		'cs' => 'Welcome to the Czech version of our website!',
		'en' => 'Welcome to the English version of our website!',
	};
});

Módulos

Se temos mais rotas que pertencem a um módulo, podemos usar withModule() para agrupá-las:

$router = new RouteList;
$router->withModule('Forum') // os seguintes roteadores fazem parte do módulo Forum
	->addRoute('rss', 'Feed:rss') // apresentador é Forum:Feed
	->addRoute('<presenter>/<action>')

	->withModule('Admin') // os seguintes roteadores fazem parte do módulo Forum:Admin
		->addRoute('sign:in', 'Sign:in');

Uma alternativa é usar o parâmetro module:

// Gestão de URLs/ mapas de default para o apresentador Admin:Dashboard
$router->addRoute('manage/<presenter>/<action>', [
	'module' => 'Admin',
]);

Subdomínios

As coleções de rotas podem ser agrupadas por subdomínios:

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

Você também pode usar wildcards em seu nome de domínio:

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

Prefixo do caminho

As coleções de rotas podem ser agrupadas por caminho na URL:

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

Combinações

O uso acima pode ser combinado:

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

Parâmetros de consulta

As máscaras também podem conter parâmetros de consulta (parâmetros após o ponto de interrogação na URL). Elas não podem definir uma expressão de validação, mas podem alterar o nome sob o qual são passadas ao apresentador:

// usar o parâmetro de consulta 'cat' como uma 'categoriaId' na aplicação
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);

Parâmetros Foo

Estamos indo mais fundo agora. Os parâmetros Foo são basicamente parâmetros anônimos que permitem corresponder a uma expressão regular. O seguinte roteiro corresponde a /index, /index.html, /index.htm e /index.php:

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

Também é possível definir explicitamente uma cadeia que será usada para a geração de URLs. A cadeia deve ser colocada diretamente após o ponto de interrogação. A seguinte rota é semelhante à anterior, mas gera /index.html ao invés de /index porque a string .html é definida como um “valor gerado”.

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

Integração

A fim de conectar nosso roteador à aplicação, devemos informar o recipiente DI sobre isso. A maneira mais fácil é preparar a fábrica que irá construir o objeto roteador e dizer à configuração do contêiner para usá-lo. Portanto, digamos que escrevemos um método para este fim 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;
	}
}

Em seguida, escrevemos em configuração:

services:
	- App\Core\RouterFactory::createRouter

Quaisquer dependências, tais como uma conexão de banco de dados, etc., são passadas para o método de fábrica como seus parâmetros usando a fiação automática:

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

SimpleRouter

Um roteador muito mais simples do que a coleta de rotas é o SimpleRouter. Ele pode ser usado quando não há necessidade de um formato URL específico, quando mod_rewrite (ou alternativas) não está disponível ou quando simplesmente não queremos nos preocupar com URLs de fácil utilização ainda.

Gera endereços de forma aproximada:

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

O parâmetro do construtor SimpleRouter é um apresentador e ação padrão, ou seja, ação a ser executada se abrirmos, por exemplo, http://example.com/ sem parâmetros adicionais.

// default para o apresentador 'Home' e ação 'default
$router = new Nette\Application\Routers\SimpleRouter('Home:default');

Recomendamos definir o SimpleRouter diretamente na configuração:

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

SEO e Canonização

A estrutura aumenta o SEO (Search Engine Optimization) ao evitar a duplicação de conteúdo em diferentes URLs. Se vários endereços forem ligados a um mesmo destino, por exemplo /index e /index.html, a estrutura determina o primeiro como primário (canônico) e redireciona os outros para ele usando o código HTTP 301. Graças a isso, os mecanismos de busca não indexarão páginas duas vezes e não quebrarão sua classificação de páginas. .

Este processo é chamado de canonização. A URL canônica é aquela gerada pelo roteador, ou seja, pela primeira rota de correspondência na coleta sem a bandeira OneWay. Portanto, na coleção, listamos primeiro caminhos primários.

A canonização é realizada pelo apresentador, mais no capítulo canonização.

HTTPS

Para utilizar o protocolo HTTPS, é necessário ativá-lo na hospedagem e configurar o servidor.

O redirecionamento de todo o site para HTTPS deve ser feito no nível do servidor, por exemplo, usando o arquivo .htaccess no diretório raiz de nossa aplicação, com o código HTTP 301. As configurações podem ser diferentes dependendo da hospedagem e se parece com isto:

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

O roteador gera uma URL com o mesmo protocolo que a página foi carregada, portanto, não há necessidade de definir mais nada.

Entretanto, se excepcionalmente precisarmos de rotas diferentes para operar sob protocolos diferentes, a colocaremos na máscara da rota:

// Irá gerar um endereço HTTP
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);

// Irá gerar um endereço HTTPS
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);

Roteador de depuração

A barra de roteamento exibida na Tracy Bar é uma ferramenta útil que exibe uma lista de rotas e também os parâmetros que o roteador obteve da URL.

A barra verde com o símbolo ✓ representa a rota que corresponde à URL atual, as barras azuis com símbolos ≈ indicam as rotas que também corresponderiam à URL se o verde não as ultrapassasse. Vemos o apresentador e a ação atual mais adiante.

Ao mesmo tempo, se houver um redirecionamento inesperado devido à canonicalização, é útil olhar na barra redireto para ver como o roteador entendeu originalmente a URL e porque ela foi redirecionada.

Ao depurar o roteador, recomenda-se abrir as Ferramentas do Desenvolvedor no navegador (Ctrl+Shift+I ou Cmd+Option+I) e desabilitar o cache no painel de Rede para que os redirecionamentos não sejam armazenados nele.

Desempenho

O número de rotas afeta a velocidade do roteador. Seu número não deve certamente exceder algumas dúzias. Se seu site tem uma estrutura URL excessivamente complicada, você pode escrever um roteador personalizado.

Se o roteador não tem dependências, como em um banco de dados, e sua fábrica não tem argumentos, podemos serializar sua forma compilada diretamente em um container DI e assim tornar a aplicação um pouco mais rápida.

routing:
	cache: true

Roteador personalizado

As linhas a seguir são destinadas a usuários muito avançados. Você pode criar seu próprio roteador e, naturalmente, adicioná-lo à sua coleção de rotas. O roteador é uma implementação da interface Nette\Routing\Router com dois métodos:

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
	{
		// ...
	}
}

O método match processa o atual $httpRequest, do qual não apenas a URL, mas também cabeçalhos etc. podem ser recuperados, em uma matriz contendo o nome do apresentador e seus parâmetros. Se não puder processar o pedido, ele retorna nulo. Ao processar o pedido, devemos retornar pelo menos o apresentador e a ação. O nome do apresentador é completo e inclui quaisquer módulos:

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

O método constructUrl, por outro lado, gera uma URL absoluta a partir da matriz de parâmetros. Ele pode utilizar as informações do parâmetro $refUrl, que é a URL atual.

Para adicionar roteador personalizado à coleta de rotas, use add():

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

Uso separado

Por uso separado, entendemos o uso das capacidades do roteador em uma aplicação que não utiliza Nette Application e apresentadores. Quase tudo o que mostramos neste capítulo se aplica a ela, com as seguintes diferenças:

Assim, mais uma vez, criaremos um método que construirá um roteador, por exemplo:

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 você usar um recipiente DI, que recomendamos, adicione novamente o método à configuração e, em seguida, obtenha o roteador junto com a solicitação HTTP do recipiente:

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

Ou criaremos objetos diretamente:

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

Agora temos que deixar o roteador trabalhar:

$params = $router->match($httpRequest);
if ($params === null) {
	// não foi encontrada nenhuma rota correspondente, enviaremos um erro 404
	exit;
}

// nós processamos os parâmetros recebidos
$controller = $params['controller'];
// ...

E vice-versa, usaremos o roteador para criar o link:

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