Durante a construção e evolução do ASP.NET MVC e Web API a Microsoft foi assumindo algumas regras, que durante a execução, os frameworks alteram e geram informações de acordo com o código escrito por nós. Isso é conhecido como convention-over-configuration, e tem como foco diminuir algumas configurações explícitas por parte do desenvolvedor, focando em detalhes mais importantes para atender o negócio e o framework se responsabiliza por realizar as configurações necessárias para o funcionamento daquele código respeitando as tais convenções.
Apesar de as vezes não notarmos, isso está bastante presente em nosso dia a dia. Quando criamos um controller, dentro dele uma ação e esta, por sua vez, retornarmos uma view, o ASP.NET MVC assume a estrutura física de Views/NomeDoController/NomeDaAcao.cshtml para armazenar o arquivo e localiza-lo para exibir para o cliente. Outro exemplo é quando utilizamos o Entity Framework e queremos que a entidade Cliente seja persistida na base de dados; o ORM irá criar, automaticamente, a tabela com o mesmo nome daquela entidade. Já no ASP.NET Web API, se criarmos uma ação com o método chamado Get, ele estará automaticamente acessível através do verbo GET do HTTP, sem precisar decorar o mesmo com o atributo HttpGetAttribute.
Note que nos dois exemplos a Microsoft assumiu essas regras e as asseguram durante a execução, ficando, de certa forma, transparente para nós. Se não houvesse isso, teríamos que configurar as mesmas coisas em 100% dos casos; com essas convenções predefinidas, precisamos apenas nos preocupar quando queremos algo diferente do padrão.
O ASP.NET MVC e Web API expõem tipos para que possamos customizar o framework de acordo com nossas necessidades. Até então, isso poderia ser realizado customizando alguns serviços de baixo nível (controller factory, por exemplo), mas o problema desta técnica é que estaremos tocando em outras atividades e que qualquer descuido poderemos ter problemas ao executar e, consequentemente, não atingirmos o objetivo esperado.
O ASP.NET 5 simplifica bastante essa customização expondo uma interface chamada IApplicationModelConvention. Ao implementar ela e acopla-la a execução, o ASP.NET identificará todos os controllers, ações, rotas e filtros candidatos a serem expostos (de acordo com a convenção predefinida) e entregará para analisarmos (através da classe ApplicationModel), e com isso, podemos customizar da forma que precisamos. Tudo isso através de uma API de mais fácil utilização, sem a necessidade de conhecer detalhes dos bastidores para implementar convenções que estão em um nível mais superficial.
Para exemplificar o uso, considere o controller abaixo, que possui duas ações e que retornam uma string. Como podemos perceber, o código foi escrito utilizando o português. Por convenção, o ASP.NET Web API assumirá que o nome dos membros (classe e métodos) serão os mesmos que serão expostos e utilizados pelo sistema de roteamento para montar os links de acesso à API e suas ações.
public class UtilitariosController : Controller
{
[HttpGet]
public string ExecutarTarefa()
{
return “…” ;
}
[HttpGet]
public string EnviarEmail()
{
return “…”;
}
}
A ideia é deixar o desenvolvedor livre para codificar no idioma desejado, e através do recurso de convenção, vamos estipular que o framework irá traduzir de acordo com a escolha realizada durante a sua configuração. Na ausência do idioma, a nomenclatura utilizada no código será exposta.
O que fazemos abaixo é no método Apply (único, exposto pela interface IApplicationModelConvention) percorremos os controllers encontrados pelo ASP.NET, procuramos pela chave no dicionário (que em um ambiente real, poderíamos estar recorrendo à algum outro repositório ao invés de hard-code), e por fim substituímos o nome do controller e da ação pelo encontrado no dicionário, baseado na cultura escolhida.
public class LocalizedApiNamingModelConvention : IApplicationModelConvention
{
private string culture;
private IDictionary<string, Tuple<string, string>> mapping =
new Dictionary<string, Tuple<string, string>>();
public LocalizedApiNamingModelConvention(string culture)
{
this.culture = culture;
mapping.Add(
“en-US.Utilitarios.ExecutarTarefa”,
Tuple.Create(“Utilities”, “RunTask”));
mapping.Add(
“en-US.Utilitarios.EnviarEmail”,
Tuple.Create(“Utilities”, “SendEmail”));
}
public void Apply(ApplicationModel application)
{
foreach (var c in application.Controllers)
{
var controllerName = c.ControllerName;
foreach (var a in c.Actions)
{
var key = BuildKey(controllerName, a.ActionName);
if (this.mapping.ContainsKey(key))
{
var info = this.mapping[key];
c.ControllerName = info.Item1;
a.ActionName = info.Item2;
}
}
}
}
private string BuildKey(string controllerName, string actionName)
{
return $”{culture}.{controllerName}.{actionName}”;
}
}
Depois da classe criada, basta incluirmos na coleção chamada Conventions, e fazemos isso através do método AddMvcCore, conforme é mostrado no código abaixo:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore(o =>
o.Conventions.Add(new LocalizedApiNamingModelConvention(“en-US”)));
}
Repare que criamos a classe e no construtor passamos a cultura en-US e ao rodar a aplicação, os links acessíveis serão /Utilities/RunTask e /Utilities/SendEmail. Os links em português retornarão o erro 404 do HTTP (Not Found).