Tabela de rotas – Parte 1

A tabela de rotas no framework ASP.NET MVC serve para mapear as URLs da aplicação para seus correspondentes controladores e ações dentro de eles.

Por Eduard Tomás - Tradução CRV


Publicado em: 01/9/11
Valorize este artigo:
Nestes primeiros quatro capítulos da série sobre ASP.NET MVC vimos os aspectos fundamentais do framework: controladores, ações e visões. No capítulo dois, vimos que cada requisição do navegador deve ser roteada para uma ação de um controlador. Dissemos também que por padrão se segue a convenção de que as URLs estão na forma http://servidor/controlador/accion.

O que muita gente não sabe é que isso não é realmente uma convenção do framework de ASP.NET MVC e muito menos uma obrigação. O fato de que as URLs sigam por padrão esta forma se deve, simplesmente, ao código gerado por padrão pelo Visual Studio quando criamos um projeto ASP.NET MVC. O responsável por decidir que ação de que controlador se encarrega de processar cada requisição é a tabela de rotas e é disso que vamos falar hoje.

Rapidamente poderíamos dizer que a tabela de rotas é a responsável por mapear cada URL para uma ação de um controlador. A frase pode parecer banal, mas não o é em absoluto. Há dois aspectos chave a levar em conta:

  1. Cada URL deve ser mapeada para uma ação de um controlador específico .
  2. Para mapear uma requisição do navegador para uma ação de um controlador só se usa a URL. Repito: só a URL.
Falando em propriedade, a frase anterior não é de todo precisa. O que a tabela de rotas realmente faz é determinar que valores de rota (route values) se tomarão a partir da URL.

O que nos leva a nos perguntar, o que são os valores de rota.

Valores de rota (route values)

Os valores de rota nada mais são do que um conjunto de valores (parâmetros, se vocês preferirem) cujo valor se obtém a partir da requisição realizada pelo navegador, especificamente a partir da URL. Pode haver todos os valores de rota que se queira, mas há dois que devem ser estabelecidos sempre por cada URL:
  1. controller: Deve conter o nome do controlador que gerenciará a requisição
  2. action: Deve conter o nome da ação que gerenciará a requisição
Esses dois valores são sempre esperados pelo framework , para assim poder rotear a requisição para uma ação de um controlador. Porém, além desses dois, podem existir outros valores de rota. E o que faz o framework com o resto de valores de rota? Os salva e os envia à ação que gerencia a requisição.

A tabela de rotas é, portanto, a responsável por decidir, por cada URL, que valores de rota, e com que valor real, serão tomadas. Lembrem que controller e action são obrigatórios e que são usados pelo framework para, precisamente, decidir que ação de que controlador gerenciará a requisição, daí que se diga comumente, que a tabela de rotas mapeia URLs para ações, embora como vimos , realmente faz algo mais.

Configuração da tabela de rotas

A tabela de rotas não tem valores por padrão. Ou seja, inicialmente está vazia e isso significa que não há nenhuma requisição que possa ser roteada. O próprio Visual Studio nos gera código para configurar a tabela de rotas, com alguns valores iniciais. E esses valores são os que fazem com que as URLs tenham a forma conhecida de http://servidor/controlador/accion. Se vocês abrirem Visual Studio e criarem um projeto ASP.NET MVC novo (inclusive se escolherem a opção de Empty), vão ver o seguinte código no arquivo Global.asax.cs:

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}

Este código é o que configura a tabela de rotas. O parâmetro routes que recebe este método é a própria tabela de rotas, que está na propriedade estática Routes da classe RouteTable.

Analisemos este código, e comecemos não pela primeira linha, mas pela segunda, a que chama o método MapRoute. Este método (que é realmente um método de extensão, ainda que isto não seja relevante) nos permite acrescentar uma nova entrada à tabela de rotas de forma extremamente simples. Está sobrecarregado mas neste código os parâmetros que recebe são:

  1. O nome da rota (um identificador da rota). Neste caso a rota se chama Default
  2. As URLs que mapeia esta rota
  3. Os valores por padrão dos valores de rota, no caso de não serem encontrados na URL.

Padrões de URLs

Centremo-nos um pouco no segundo parâmetro. Seu valor é “{controller}/{action}/{id}”. Isso é simplesmente o padrão que devem cumprir as URLs para ser processadas por esta rota. O que está entre chaves é o nome do valor de rota criada. Assim, o padrão {controller}/{action}/{id} mapeará qualquer URL que esteja na forma http://servidor/xxx/yyy/zzz. E ademais atribuirá os seguintes valores de rota:
  1. controller = xxx
  2. action = yyy
  3. id = zzz
Mas… o que acontece com uma URL que não tenha o /zzz final? O que ocorre com uma URL http://servidor/xxx/yyy? Bom, em princípio uma URL deste tipo não seria processada por este padrão (já que o padrão pede explicitamente que haja {controller}/{action}/{id}. Porém, para evitar que a configuração da tabela de rotas custasse muito, existe o terceiro parâmetro: os valores por padrão.

Valores por padrão

O terceiro parâmetro de MapRoute são os valores por padrão dos valores de rota. Ou seja, se o valor de rota não pode ser obtido a partir do padrão de URL especificado, se assumirá tal valor. Define-se como um objeto anônimo cujas propriedades têm o nome dos valores de rota e o valor dessa propriedade é o valor por padrão do valor de rota. No nosso caso, está definido como:

new { controller = "Home", action = "Index", id = UrlParameter.Optional }

Portanto temos :

  • O valor de rota “controller” vale “Home” por padrão.
  • O valor de rota “action” vale “Index” por padrão
  • O valor de rota “id” é opcional. Ou seja, se não aparece não será criado(não existirá). Não é que tenha valor null, 0, cadeia vazia ou qualquer outro valor vazio, não. Simplesmente não será criado.
Visto isto, agora podemos observar que:
  1. http://servidor/Books/View/10 será processado e os valores de rota serão
    1. controller = Books
    2. action = View
    3. id = 10
  2. http://servidor/Books será processado e os valores de rota serão
    1. controller = Books
    2. action = Index (valor por padrão)
    3. id = Não existirá o valor de rota “id”
  3. http://servidor será processado e os valores de rota serão
    1. controller = Home (valor por padrão)
    2. action = Index (valor por padrão)
    3. id = Não existirá o valor de rota “id”
  4. http://servidor/Books/View/10/20 Não será processada pela tabela de rotas. Esta URL não pode ser mapeada pelo padrão {controller}/{action}/{id}
Se uma URL não pode ser processada pela tabela de rotas, o framework retorna um erro http 404 (página não encontrada).

Múltiplos padrões

A tabela de rotas se chama justamente tabela porque pode conter várias entradas (ou seja, vários padrões de URL, com seus parâmetros por padrão, etc). Para acrescentar mais entradas (rotas, cada entrada se conhece como rota), o mais simples é acrescentar chamadas para MapRoute. P.ex. se quiséssemos processar a URL anterior http://servidor/Books/View/10/20 poderíamos acrescentar uma entrada adicional:

routes.MapRoute(
"DosIds", // Route name
"{controller}/{action}/{id}/{id2}"
);

Agora a URL http://servidor/Books/View/10/20 já pode ser processada, e será processada por essa entrada nova na tabela de rotas. Os valores de rota criados serão:
  • controller = Books
  • action = View
  • id = 10
  • id2 = 20
Um assunto importante é que a ordem das rotas na tabela de rotas importa. Por cada URL, o framework avaliará as distintas entradas da tabela de rotas, uma a uma, na ordem em que estas se encontrem e tão logo uma URL possa ser processada, se escolherá essa entrada da tabela de rotas. P.ex, suponhamos que queremos mapear as URLs da forma http://servidor/Ver/Edu para ação View do controlador Profile com um valor de rota chamado user cujo valor seja o que houver depois de Ver (Edu, neste caso). Isso pode ser conseguido com uma entrada na tabela de rotas:

routes.MapRoute(
"ViewProfile", // Nome da rota
"Ver/{author}", // URL with parameters
new { controller = "Profile", action = "View" }
);

Um pequeno detalhe a ser levado em conta desta nova entrada é que dado que não há lugar no padrão de URL para os valores de rota de controller e action, por serem obrigatórios, devem especificar-se como valores por padrão.

Esta entrada mapeia uma URL do tipo: http://servidor/Ver/Edu com os valores de rota:

  • controller = Profile (valor por padrão)
  • action = View (valor por padrão)
  • author = Edu (obtido do padrão da URL)
Porém, vocês podem comprovar que se acrescentarmos essa linha depois do routes.MapRoute que já havia e entrarmos com uma URL do tipo http://servidor/Ver/Edu o framework retornará um 404, mesmo que vocês tenham o controlador Profile com uma ação View definida.

Por que?

Simplesmente porque a tabela de rotas é avaliada em ordem. E pode a primeira entrada mapear (a rota chamada Default) uma URL do tipo http://servidor/Ver/Edu? A resposta é sim, e os valores de rota ficam estabelecidos a :

  • controller = Ver
  • action = Edu
  • id = Não há valor de rota “id” (lembrem que era opcional)
Portanto, a não ser que vocês tenham um controlador chamado Ver com uma ação chamada Edu (coisa pouco provável, não nos enganemos:p) o framework devolverá um 404. Para que tudo funcione, a entrada “Default” deve estar depois da nova entrada:

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
"ViewProfile", // Nome da rota
"Ver/{author}", // URL with parameters
new { controller = "Profile", action = "View" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new {controller = "Home", action = "Index", id = UrlParameter.Optional} // Parameter defaults
);
}

Ignorar padrões de URL

Às vezes, é necessário que certas requisições com uma determinada URL, não sejam gerenciadas pelo framework de ASP.NET mas sim pelo próprio motor de ASP.NET ou então pelo próprio IIS. Um exemplo poderia ser a requisição de uma imagem. Neste caso, não vamos querer um controlador que nos retorne a imagem; é muito melhor deixar o próprio IIS fazê-lo (será muito mais eficiente). Por isso, o framework permite definir rotas de exclusão, ou melhor, rotas que caso mapeiem uma determinada URL, tal solicitação será ignorada por ASP.NET MVC.

Para criar essas rotas especiais, usa--se o método IgnoreRoute. Costuma-se passar um só argumento, que é o padrão de URL a ser ignorado. Temos um exemplo no próprio código gerado pelo Visual Studio:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

Este padrão de URL será mapeado a todas aquelas URLs que tenham a forma http://servidor/xxx.axd/yyy
Onde:

  1. xxx é qualquer nome
  2. yyy é qualquer coisa, incluindo barras separadoras (/). O fato de que yyy possa incluir barras separadoras é porque se usa a forma {*nome_valor_rota} (com um asterisco) que é o que se conhece como catch-all e significa literalmente: captura todos os caracteres da URL que venham a partir de agora.
Ou seja, a URL http://servidor/trace.axd/foo/bar/baz será roteada por esta rota. Ao ser declarada com IgnoreRoute, isso vai fazer com que essa solicitação seja ignorada por ASP.NET MVC e seja processada por alguém mais (neste caso o próprio motor de ASP.NET, mas isso já depende de cada caso).





Comentários do artigo
Foi enviado 1 comentário ao artigo
1 comentário não revisado
0 comentários revisados

Usuários :    login / registro

Manuais relacionados
Categorias relacionadas
O autor

Home | Sobre nós | Copyright | Anuncie | Entrar em contato