ASP.NET MVC
-
Upload
grupo-lidel -
Category
Documents
-
view
225 -
download
1
description
Transcript of ASP.NET MVC
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
222 1RROOTTEEAAMMEENNTTOO
O roteamento desempenha um papel importante na interceção e redireciona-
mento de pedidos HTTP para a plataforma ASP.NET MVC. Ao longo deste
capítulo, vamos analisar algumas das particularidades associadas ao roteamento
de pedidos em ASP.NET MVC.
2.1 UURRLL
Um URL (Uniform Resource Locator1) representa um endereço que identifica
univocamente um recurso numa determinada rede. Na prática, um URL segue
sempre uma estrutura do tipo protocolo://máquina/caminho/recurso. No caso das
aplicações Web, os URL foram usados durante anos para identificar recursos
existentes num servidor (por exemplo, o índice da especificação do HTML5 pode
ser obtida a partir de http://dev.w3.org/html5/spec/Overview.html).
O leitor com experiência no desenvolvimento de aplicações Web no
“universo” Microsoft recorda, com toda a certeza, o uso de URL do tipo
http://www.minhaempresa.com/filmes/lista.aspx?categoria=1. Nestes casos, era
quase certa a existência de uma página ASPX, designada por lista.aspx, que seria
responsável por tratar pedidos desse género, tendo em atenção o parâmetro
passado na query string. Ou seja, no mundo ASP.NET pré-MVC, era normal
existir um relacionamento direto entre um URL e um recurso físico (geralmente,
uma página ASPX ou uma handler) existente no disco. Com a introdução da
plataforma ASP.NET MVC, este relacionamento entre URL e recurso físico já não
existe, uma vez que o mapeamento passa a ser estabelecido entre um URL e um
1 Se quisermos ser precisos, um URL é um tipo concreto de URI (Uniform Resource Identifier). Contudo,
e na prática, ambos são muitas vezes usados como sinónimos. O leitor interessado pode obter mais detalhes em
http://bit.ly/TzQ7w.
AASSPP..NNEETT MMVVCC
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
26
método de ação de um controlador (os métodos públicos de um controlador que
são responsáveis por tratar pedidos HTTP são designados por métodos de ação).
Por exemplo, no caso de aplicações ASP.NET MVC, é normal encon-
trarmos URL do tipo http://www.minhaempresa.com/filmes/categoria/1. Estes
URL são intercetados pela infraestrutura de roteamento, que se encarrega de os
redirecionar (indiretamente) para um método de ação de um controlador, que,
por sua vez, é responsável por gerar a resposta devolvida ao browser.
Note-se que esta forma de definir URL permite-nos ainda melhorar a
usabilidade2 associada aos URL de uma aplicação (se compararmos os dois URL
anteriores, rapidamente concluimos que o último é mais fácil de memorizar e
transmite mais informação acerca do recurso devolvido – estas são apenas duas
boas caraterísticas de um URL).
2.2 IINNTTRROODDUUÇÇÃÃOO AAOO RROOTTEEAAMMEENNTTOO
A plataforma ASP.NET recorre ao roteamento para atingir dois objetivos:
Mapear URL de pedidos HTTP em métodos de ação disponibilizados
por um controlador;
Gerar URL que, no futuro, produzam pedidos HTTP que sejam
mapeados num método de ação de um controlador.
Roteamento versus URL rewriting
O leitor com experiência no uso de URL rewriting pode ser tentado a ver o roteamento
como uma nova forma de efetuar a reescrita de URL (a reescrita de URL permite-nos criar
URL amigáveis que melhorem os resultados devolvidos pelos motores de pesquisa – esta
é uma das técnicas mais usadas nas otimizações SEO3). Existe, contudo, uma diferença
importante entre ambas as técnicas. O URL rewriting concentra-se apenas em transformar
um URL num outro URL. Por sua vez, o roteamento é responsável por mapear um URL
2 Jakob Nielsen (um guru do estudo da usabilidade) estudou a influência de URL na acessibilidade de
aplicações. O leitor interessado pode obter algumas recomendações sobre a geração destes identificadores em
http://bit.ly/9x0mm.
3 SEO (Search Engine Optimizations) descreve um processo para melhorar a visibilidade de links de sites
nas pesquisas efetuadas por motores de busca. O link http://bit.ly/1oIpqX efetua uma boa introdução a este
tópico.
RROOTTEEAAMMEENNTTOO
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
27
num determinado recurso existente na aplicação (como veremos, o módulo4 de
roteamento encarrega-se de associar um URL a uma handler5, que, por sua vez, instancia
um controlador e executa um dos seus métodos, de forma a obter a vista que gerará o
HTML devolvido ao browser).
Para além dessa diferença, existe ainda outra: ao contrário do URL rewriting, o
roteamento usado em ASP.NET MVC permite a geração de URL compatíveis com
métodos expostos pelos controladores. Por outras palavras, podemos interagir com a
infraestrutura de roteamento e pedir-lhe para gerar um URL que “represente” um
determinado método de ação de um controlador.
Em ASP.NET MVC, precisamos de ter pelo menos uma rota ativa para
que um URL seja redirecionado para um método de ação de um controlador.
Uma rota é sempre caraterizada por um padrão, que é usado para verificar a sua
compatibilidade com o URL de um pedido HTTP. Este padrão pode, ou não,
conter parâmetros cujos valores serão apenas obtidos em runtime, depois de uma
rota ter sido considerada compatível com o URL de um pedido HTTP (na prática,
podemos pensar nos parâmetros como placeholders que serão substituídos pelos
valores indicados num URL compatível com o padrão). Quando o padrão contém
parâmetros, então a rota pode ainda indicar valores predefinidos e restrições
aplicáveis aos mesmos. Finalmente, importa ainda referir que uma rota dever ser
sempre identificada univocamente através de nome (definido durante o seu
registo na tabela de roteamento).
Processamento de várias rotas
Numa aplicação ASP.NET MVC, é normal definirmos várias rotas. Neste caso, a pesquisa
de uma rota termina quando a infraestrutura de roteamento encontra a primeira rota
compatível na tabela de rotas (coleção que contém todas as rotas previamente registadas).
Portanto, devemos ter algum cuidado na definição de rotas e garantir sempre que as rotas
mais específicas são efetivamente registadas na tabela de roteamento antes das rotas
generalistas.
4 Todos os pedidos HTTP tratados pela plataforma ASP.NET passam por várias fases. Estas fases
constituem a chamada pipeline ASP.NET. Os módulos são classes que podem intercetar uma destas fases para
modificar a informação do pedido atual. O leitor interessado pode obter mais detalhes sobre a construção destes
elementos em http://bit.ly/c96Wfw.
5 As handlers são classes responsáveis por devolver a resposta a um pedido HTTP. Em ASP.NET Web Forms,
todas as páginas ASPX são handlers. Em ASP.NET MVC, existe uma handler predefinida que trata todos os pedidos
efetuados através da interação com um método de ação de um controlador. O artigo existente em http://bit.ly/rY7ExF é
um bom ponto de partida para os interessados em obter mais informações sobre estes elementos.
AASSPP..NNEETT MMVVCC
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
28
2.2.1 CCRRIIAAÇÇÃÃOO DDEE UUMMAA RROOTTAA
As rotas devem ser sempre criadas e registadas no início de uma
aplicação Web. É por isso que um projeto ASP.NET MVC gerado através do
template de Internet (ou de Intranet) invoca o método estático RegisterRoutes a
partir do método Application_Start definido no ficheiro global.asax.cs (por seu
lado, este é executado apenas uma vez, quando o primeiro recurso ASP.NET é
pedido). Por predefinição, o método RegisterRoutes contém código semelhante
ao seguinte:
public static void RegisterRoutes(RouteCollection routes){
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // nome da rota
"{controller}/{action}/{id}", //padrão URL com 3 parâmetros
new { controller = "Home", //valores predefinidos parâmetros
action = "Index",
id = UrlParameter.Optional }
);
}
A variável routes identifica a tabela de roteamento (representada
programaticamente por um objeto do tipo RouteCollection) que contém todas
as rotas, que, depois, são consumidas pelo módulo de roteamento. Concentremo-
-nos, para já, no método MapRoute. Na sua forma mais simples, este método
obriga-nos apenas a definir o nome da rota e o respetivo padrão. O excerto
seguinte ilustra o uso desta forma:
Routes.MapRoute( "teste",
"{parametro1}/{parametro2}/{parametro3}");
O padrão apresentado no excerto anterior define três parâmetros
(parametro1, parametro2 e parametro3). Na prática, um parâmetro é sempre
delimitado por { } e serve para identificar um segmento do URL cujo valor será
obtido em runtime. Não existem grandes restrições quanto aos nomes que podem
ser usados para identificar os parâmetros definidos num padrão. Na prática, con-
tudo, convém apenas utilizarmos nomes que possam ser usados como identi-
ficadores em C#, se quisermos que os seus valores alimentem automaticamente
RROOTTEEAAMMEENNTTOO
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
29
eventuais parâmetros disponibilizados por um método de ação (a secção 2.2.2
apresenta mais pormenores sobre estas operações).
Ao intercetar um pedido, o módulo de roteamento é responsável por
verificar se o URL desse pedido é, ou não, compatível com alguma das rotas
previamente registadas na tabela de roteamento. Quando uma rota é considerada
compatível, então o módulo tem de obter os valores dos eventuais parâmetros do
padrão da rota a partir desse URL. A Tabela 2.1 procura ilustrar o processo de
obtenção de valores de parâmetros definidos num URL padrão.
URL VALORES DE PARÂMETROS
/filmes/cat/1 parametro1="filmes"
parametro2="cat"
parametro3="1"
/filmes/pt-PT/1 parametro1="filmes"
parametro2="pt-PT"
parametro3="1"
TTAABBEELLAA 22..11 –– Obtenção de valores de parâmetros a partir de um URL
Como a rota teste (apresentada no excerto de código anterior) define
três parâmetros, então é compatível com qualquer URL definido à custa de três
segmentos (um segmento é uma porção de texto delimitado por uma barra /).
Como é possível observar através do segundo exemplo apresentado na Tabela
2.1, o caráter / é o único que, efetivamente, delimita um segmento (repare-se
como pt-PT é atribuído ao segundo parâmetro, tendo o módulo simplesmente
ignorado o uso do caráter –).
Depois de obter os valores dos parâmetros a partir do URL do pedido
atual, o módulo de roteamento encarrega-se ainda de os guardar num dicionário
do tipo RouteValueDictionary (o nome de cada parâmetro é usado como chave
de cada entrada efetuada no dicionário). Como é óbvio, este dicionário pode ser
recuperado mais tarde através do contexto de roteamento representado por uma
instância do tipo RequestContext6 (voltaremos a este contexto mais tarde).
6 Como veremos, esta informação pode ser recuperada a partir de código usado num controlador ou
numa vista.
AASSPP..NNEETT MMVVCC
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
30
2.2.2 PPAARRÂÂMMEETTRROOSS EESSPPEERRAADDOOSS PPEELLOO AASSPP..NNEETT MMVVCC
Como referimos, a plataforma ASP.NET MVC espera o uso de
parâmetros com determinados nomes. Todas as rotas reencaminhadas para
plataforma devem, pelo menos, definir dois parâmetros, designados por
controller e action. O valor de controller identifica o nome da classe
(controlador) que disponibiliza o método (de ação) identificado pelo valor do
segundo parâmetro (action). Por convenção, a plataforma encarrega-se de
adicionar o prefixo Controller ao valor do parâmetro e usa esse nome para
procurar um tipo que implementa a interface IController (note-se que a
capitulação não é tida em conta nesta pesquisa).
O parâmetro action e a interface IController
O parâmetro action é apenas usado para identificar o método de ação quando o
controlador em causa reutiliza a classe base Controller. Controladores que
implementem diretamente a interface IController são responsáveis por definir as suas
próprias convenções e mapeamentos entre rotas e controladores.
Como na maior parte das vezes a criação de um novo controlador implica a extensão da
classe Controller, então é normal dizermos que temos de garantir sempre a existência
de um parâmetro de roteamento designado por action.
Portanto, a rota que usámos na secção 2.1 acaba por não ser uma rota
MVC válida. Para a usarmos numa aplicação ASP.NET MVC, temos de alterá-la
por forma a garantir que um pedido inicializa sempre os parâmetros controller
e action. O excerto seguinte ilustra uma forma de a tornamos numa rota que
pode ser interpretada pela plataforma ASP.NET MVC:
Routes.MapRoute( "teste",
"{controller}/{action}/{parametro3}");
Se reutilizamos o primeiro URL apresentado na Tabela 2.1, então é
possível afirmarmos que o URL /filmes/cat/1 será tratada pelo método Cat
exposto pela classe FilmesController. Apesar de o segundo URL apresentado
pela tabela ser compatível com o padrão da regra anterior, a verdade é que a rota
obtida por ele não conduzirá à execução de um método de ação, uma vez que não
é possível termos um método designado por pt-PT em C# (no Capítulo 3,
veremos como é que podemos associar um nome C# inválido a um método de
ação). Ou seja, neste segundo exemplo, o problema não reside na obtenção de
uma rota compatível, mas sim no facto de o valor do parâmetro action não
corresponder a um nome válido em C#.
RROOTTEEAAMMEENNTTOO
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
31
2.2.3 MMAAPPEEAAMMEENNTTOO DDEE PPAARRÂÂMMEETTRROOSS DDEE RROOTTEEAAMMEENNTTOO EEMM
PPAARRÂÂMMEETTRROOSS DDOO MMÉÉTTOODDOO DDEE AAÇÇÃÃOO
A plataforma ASP.NET MVC consegue ainda transformar os restantes
parâmetros de roteamento indicados por um padrão da rota em parâmetros do
método de ação que será invocado. Para que isto aconteça, os nomes dos
parâmetros do método de ação têm de ser os mesmos que os especificados pelo
padrão da rota e a plataforma tem de conseguir converter as strings obtidas a
partir do URL atual nos tipos dos parâmetros usados nos métodos de ação. Por
exemplo, suponhamos que o método Cat é definido da seguinte forma:
public class FilmesController:Controller {
public ActionResult Cat(Int32 parametro3) {
//obtem filmes da categoria
return View();
}
}
Ao encontrar a informação processada pelo módulo de roteamento a
partir do URL /filmes/cat/1, a plataforma ASP.NET MVC acaba por atribuir o
valor 1 ao parâmetro parametro3 durante a invocação do método Cat. Isto só é
possível porque ambos os parâmetros (o de roteamento e o definido pelo método
de ação) possuem o mesmo nome e porque a plataforma consegue converter o
valor extraído do URL (uma string) no tipo esperado pelo parâmetro do método
de ação (voltaremos a esta conversão noutra secção).
Antes de avançarmos, importa ainda referir que todo o trabalho de
mapeamento de parâmetros de roteamento em parâmetros de métodos é feito
pela plataforma ASP.NET MVC a partir do dicionário de valores preenchido pela
infraestrutura de roteamento. Ou seja, apesar de estarmos a falar acerca destes
mapeamentos neste capítulo, a verdade é que o trabalho desempenhado pela
infraestrutura de roteamento limita-se apenas a preencher o dicionário de dados
com informação obtida a partir do URL do pedido HTTP. Este dicionário é
depois usado por um componente especial, designado por model binder, para
inicializar os parâmetros passados ao método de ação responsável pelo
tratamento do pedido HTTP atual (voltaremos a este tópico no Capítulo 3).
AASSPP..NNEETT MMVVCC
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
32
2.2.4 UUSSOO DDEE VVAALLOORREESS LLIITTEERRAAIISS
Apesar de todos os exemplos apresentados até agora terem usado
parâmetros nos segmentos, a verdade é que o padrão de uma rota também pode
conter valores literais7. O excerto seguinte ilustra este cenário:
/historico/{controller}/{action}/{parametro3}
Para que um URL seja compatível com a regra anterior, tem de começar
sempre pela string /historico/. Por exemplo, o URL /historico/Filmes
/Cat/1 é compatível com este padrão, mas o URL /Filmes/Cat/1 já não é.
Importa ainda referir que um segmento pode ter um ou mais parâmetros,
que podem, ou não, ser misturados com valores literais. A única restrição
prende-se com o facto de dois parâmetros não poderem ser definidos
consecutivamente. O excerto seguinte ilustra estes cenários:
/historico/{lingua}-{pais}/{controller}/{action} //válido
{controller}/{action}.{outroValor} //válido: . separa parâmetros
{controller}{action}/{id} //inválido: dois parâmetros seguidos
2.2.5 PPAARRÂÂMMEETTRROO CCAATTCCHH AALLLL
A infraestrutura de roteamento permite ainda o mapeamento de vários
segmentos de um URL num único parâmetro de roteamento, através do uso de
um parâmetro catch all. O parâmetro catch all é um parâmetro especial, cujo nome
é prefixado obrigatoriamente pelo caráter *. O padrão de uma rota só pode ter
um parâmetro deste tipo e este parâmetro, quando definido, deve ocupar sempre
o seu último segmento. O exemplo seguinte ilustra o uso deste tipo de
parâmetros no padrão de uma rota:
Routes.MapRoute( "teste",
"{controller}/{action}/{*extra}");
O excerto seguinte apresenta vários URL e explica a forma como os
parâmetros são inicializados a partir do parsing do URL (supondo que a regra
apresentada no excerto anterior é a única presente na tabela de roteamento):
URL: /Filmes/Cat/Opcao/Outra
7 Em computação, a expressão “valor literal” é usada para representar um valor constante no código
de uma aplicação. Por exemplo, a instrução int a = 10; inicializa a variável a com o valor literal 10.
RROOTTEEAAMMEENNTTOO
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
33
//controller = "Filmes"
//action = "Cat"
//extra = "Opcao/Outra"
URL: /Filmes/Cat
//controller = "Filmes"
//action = "Cat"
//extra = ""
Note-se como o segundo URL apresentado no exemplo é compatível com
a regra de roteamento "{controller}/{action}/{*extra}", sendo o
parâmetro extra inicializado com uma string vazia.
2.2.6 VVAALLOORREESS PPRREEDDEEFFIINNIIDDOOSS
O algoritmo de mapeamento de um URL numa rota depende ainda da
atribuição de valores predefinidos aos parâmetros. Para ilustrar este ponto,
vamos começar por alterar a rota que temos usado até aqui:
Routes.MapRoute( "teste",
"{controller}/{action}/{parametro3}",
new {parametro3 = UrlParameter.Optional});
A atribuição de um valor predefinido ao parâmetro parametro3 aumenta
o âmbito de compatibilidade dos URL. Como vimos nas secções anteriores, a
inexistência de um valor predefinido fazia com que um URL só fosse compatível
se definisse explicitamente o valor de todos os parâmetros definidos pelo padrão.
A partir da altura em que atribuímos um valor predefinido a um parâmetro, a
definição desse parâmetro num URL deixa de ser obrigatória para que ele seja
considerado compatível com um padrão de uma rota. Portanto, a partir de agora,
todos os URL apresentados no excerto seguinte são compatíveis com a nossa
regra teste:
/Filmes/Cat/1 //parametro3 = 1
/Filmes/Cat //parametro3 não definido no dicionário
A atribuição do valor UrlParameter.Optional pode causar alguma
estranheza à primeira vista. Porque não atribuir uma string vazia ("")? Apesar de
o resultado final ser muito semelhante, existe uma diferença importante entre
ambas as opções: o uso do valor UrlParameter.Optional faz com que o
AASSPP..NNEETT MMVVCC
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
34
dicionário preenchido pelo módulo de roteamento não contenha uma entrada
para o parâmetro parametro3. Se tivéssemos optado pela string vazia, o
dicionário acabaria por ter uma entrada para o parâmetro inicializada com a
string (vazia).
Como é óbvio, não estamos limitados a definir apenas um valor por
predefinição. No excerto seguinte, modificamos a nossa rota, por forma a
garantir que todos os parâmetros usados possuem valores predefinidos:
Routes.MapRoute( "teste",
"{controller}/{action}/{parametro3}",
new {
controller = "Filme",
action = "Cat",
parametro3 = UrlParameter.Optional});
A partir desta altura, qualquer um dos URL apresentados no excerto
seguinte é compatível com a nossa rota teste:
URL: /Filme/Cat/1
//controller="Filme"
//action="Cat"
//parametro3=1
/Filme
//controller="Filme"
//action="Cat"
//parametro3 inexistente no dicionário
/
//controller="Filme"
//action="Cat"
//parametro3 inexistente no dicionário
Qualquer um dos URL apresentados na lista anterior acaba por ser
“redirecionado” para o método Cat exposto pela classe FilmeController. Nesta
altura, importa salientar que a atribuição de um valor predefinido a um
parâmetro obriga-nos a atribuir valores predefinidos a todos os parâmetros
seguintes definidos no padrão. Por exemplo, e recuperando a nossa rota teste
(que usa o padrão {controller}/{action}/{parametro3}), se atribuímos um
RROOTTEEAAMMEENNTTOO
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
35
valor predefinido ao parâmetro action, então temos também de atribuir um ao
parâmetro parametro3. A rota seguinte não respeita esta regra, já que atribui
apenas um valor predefinido ao parâmetro action:
Routes.MapRoute( "teste",
"{controller}/{action}/{parametro3}",
new {
action = "Cat"
});
Se não respeitarmos esta regra, a plataforma acaba por ignorar os valores
predefinidos quando testa a compatibilidade de um URL.
Tenho mesmo de definir os parâmetros controller e action no URL?
A atribuição de valores predefinidos aos parâmetros faz com que isso deixe de ser
obrigatório. O excerto seguinte ilustra esta estratégia:
Routes.MapRoute( "outroTeste",
"historico/{action}/{id}",
new {controller = "Filmes",
action = "Cat",
id = UrlParameter.Optional } );
Apesar de o padrão do URL não usar o parâmetro controller, a verdade é que o
dicionário de parâmetros gerado pelo módulo de roteamento contém entradas
identificadas pelos nomes controller e action. A partir desta altura, a plataforma
ASP.NET MVC tem toda a informação necessária para reencaminhar o pedido atual para
o controlador adequado.
2.2.7 AAPPLLIICCAAÇÇÃÃOO DDEE RREESSTTRRIIÇÇÕÕEESS
Se quisermos, podemos ainda definir restrições extra numa rota, que
influenciam a sua compatibilidade com o URL de um pedido. Por exemplo,
suponhamos que registamos uma rota semelhante à seguinte:
Routes.MapRoute( "teste",
"{controller}/{action}/{parametro3}",
new {
controller = "Filme",
AASSPP..NNEETT MMVVCC
©© FFCCAA –– EEddiittoorraa ddee IInnffoorrmmááttiiccaa
36
action = "Cat",
parametro3 = UrlParameter.Optional
});
A informação definida no registo da rota anterior não é suficiente para,
por exemplo, especificar que o parâmetro parametro3 só pode receber um
número inteiro. Esse requisito pode ser explicitado através do uso de uma
expressão regular8 aplicada a esse parâmetro, conforme ilustrado através do
excerto seguinte:
Routes.MapRoute( "teste",
"{controller}/{action}/{parametro3}",
new {
controller = "Filme",
action = "Cat",
parametro3 = UrlParameter.Optional
},
new{parametro3 = @"\d+"}
);
O overload do método MapRoute apresentado no excerto anterior recorre a
uma expressão regular para garantir que a rota é considerada compatível apenas
com URL onde o parâmetro de roteamento parametro3 é definido à custa de um
número inteiro (note-se ainda como a restrição é associada ao parâmetro através
da introdução de um novo objeto anónimo, onde cada propriedade identifica o
nome do parâmetro de roteamento ao qual a restrição indicada deve ser
aplicada).
O leitor com experiência em expressões regulares não deixará de observar
que a regra \d+ não é compatível só com valores numéricos, já que, por exemplo,
aceita strings do tipo asd123aa (obviamente, não era isso que pretendíamos
quando aplicámos a regra). A solução para este problema seria simples e passaria
pela adição dos carateres de início e final de linha à expressão anterior. Contudo,
isso não é necessário, uma vez que a infraestrutura de roteamento encarrega-se
8 As expressões regulares podem ser vistas como uma minilinguagem que permite efetuar operações
de pesquisa e substituição sobre texto. O leitor interessado pode obter mais detalhes sobre estas expressões em
http://bit.ly/bPCm7j.