Mapeamento distribuído

Nas partes anteriores (se você ainda não viu, veja aqui a parte 1 e a parte 2 desse texto) vimos o que é o mecanismo de rotas do ASP.NET MVC e também como o mecanismo funciona para redirecionar as URLs amigáveis inseridas no navegador para os métodos dos seus controladores. Além disso, descobrimos como fazer com que nossas aplicações web em ASP.NET utilizem rotas personalizadas, ou seja, como fazer com que padrões personalizados de URLs vindos do cliente sejam mapeados para as ações que você desejar.

E então terminamos a segunda parte supondo um cenário: tem outra equipe desenvolvendo o Front-End de uma aplicação paralelamente ao seu desenvolvimento do Back-End. Este Front-End será um cliente do serviço que você está desenvolvendo e eles esperam fazer requisições a URLs específicas para cada método chamado.

Você pode, portanto, utilizar o Web API 2 para desenvolver um serviço REST, por exemplo. Neste caso, poderia criar um novo projeto Web API ou, se for de sua preferência e achar suficiente, pode utilizar seu projeto MVC mesmo, visto que o mecanismo dos controladores dos dois frameworks é semelhante. Para tal, basta apenas garantir que o global.asax possua pelo menos a seguinte linha de código no evento Application_Start e pelo menos a seguinte classe exista na pasta App_Start.

dan10

dan11

Como podemos ver, o Web API também possui um mecanismo de roteamento bastante semelhante ao do ASP.NET MVC, mas lembre-se, ele é definido em um arquivo de configuração diferente.

Voltando ao nosso cenário, temos que fazer com que cada método do seu serviço inteiro, de qualquer controlador, possua cada um sua URL específica. Então, o que você faz? Registra uma rota para cada método? A resposta é: sim!

— Então eu terei uma classe de configuração gigante, com 50 rotas mapeadas no mesmo método?

A resposta é: não necessariamente. Pode, mas não precisa ser. Observe que o método de registro do WebApiConfig já possui como padrão a linha “config.MapHttpAttributeRoutes”. Isto permite a criação de rotas de forma distribuída, através de atributos utilizados para decorar os controladores e/ou seus métodos.

Ao se decorar um método com o atributo “Route”, define-se para ele uma rota a partir da raiz do servidor. Esta rota possui, de maneira simplificada, todos os elementos da rota registrada através do método MapRoute.

dan12

Por enquanto, vamos ignorar o atributo RoutePrefix e vamos supor que somente o método GetVehiclesInFence possua seu atributo Route. Observe também que a rota possui uma variável com o nome do parâmetro do método, tal qual as variáveis em rotas registradas pelo MapRoute. A primeira diferença é que não é necessário definir um valor para controller e action, visto que o atributo já está na ação a se executar. Assim, chamando “http://suaaplicacao.com/10/vehicles” o método acima seria executado com fenceId = 10 e seu resultado seria retornado ao cliente.

Continuando, se quisermos passar um valor default para as variáveis, basta adicionar o valor com uma sintaxe semelhante a argumentos default em C#. Assim, no exemplo acima, se quisermos que fenceId possua um valor default, poderíamos definir a rota como “{fenceId:int = 10}/vehicles”. No entanto, assim como argumentos default em C#, não seria possível fazer uma chamada a este método utilizando este valor, visto que a variável não é o último elemento da rota. Assim, seria impossível fazer uma requisição a, por exemplo, “http://suaaplicacao.com//vehicles”. Isto não mapeia para uma URL válida. Se ajustarmos a rota para “vehicles/{fenceId:int=10}”*, poderíamos chamar “http://suaaplicacao.com/vehicles”, e então a requisição seria mapeada para o método GetVehiclesInFence, com o parâmetro fenceId avaliado em 10.

Se quiser que seu valor default seja “UrlParameter.Optional”, basta adicionar o wildcard  ? à variável. Assim, poderíamos aplicar à rota “vehicles/{fenceId:int?}” e mudar o método para receber int? fenceId. Dessa forma, a chamada “http://suaaplicacao.com/vehicles” mapearia para a ação GetVehiclesInFence com o parâmetro fenceId = null. Sem o símbolo ?, o parâmetro se tornaria obrigatório e a mesma chamada não seria mapeada para lugar nenhum.

A aplicação de constraints ocorre inline através do símbolo “:”. Nosso exemplo possui um constrint na variável fenceId, indicando que somente valores numéricos inteiros serão aceitos como válidos para o mapeamento da requisição. Na imagem abaixo estão representados os constraints aplicáveis.

dan13

Fonte: http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx

 

Há ainda o atributo RoutePrefix aplicável a um controlador. Este atributo define um prefixo para qualquer chamada a uma ação daquele controlador. Assim, no nosso exemplo, para efetivamente chamar o método GetVehiclesInFence, devemos utilizar a URL “http://suaaplicacao.com/fences/10/vehicles”. Qualquer outro método definido dentro do controlador Fences será mapeado a partir do endereço “http://suaaplicacao.com/fences/”.

Pode-se também aplicar o atributo Route à classe controlador. Para tal, é necessário que a rota possua uma variável “{action}”, podendo este ter um valor default para o caso de nenhum ser inserido. Com isso é possível aplicar um método padrão ao controlador, que assim, se somente o prefixo do controlador estiver presente na requisição, sem qualquer outra especificação de ação, o valor default de action é avaliado e o método de nome correspondente é chamado.

dan14

Por fim, o mesmo tipo de roteamento distribuído por atributos é aplicável aos controladores ASP.NET MVC. Os atributos decoram as classes e métodos da exata mesma forma e para que o mapeamento ocorra adequadamente, deve-ser adicionar a linha “routes.MapMvcAttributeRoutes()” ao método RegisterRoutes da classe RouteConfig.

dan15

E com isto aprendemos como mapear nossas URLs específicas, amigáveis e totalmente independentes dos nomes das nossas classes e métodos para os locais que desejarmos. Agora o poder está em suas mãos, você tem o controle sobre os endereços pelos quais seus usuários navegarão, crie suas aplicações e serviços com responsabilidade.

* Importante manter o valor default na rota sem espaços.

Por: Daniel Prata e José Vicente
Revisão: Jéssica Saliba