Exportar (0) Imprimir
Expandir Tudo

Configurar Rotas no ASP.NET

Renato Haddad

Agosto 2013

Dn423988.060DE5057573180CEC6D227C6D3E2207(pt-br,MSDN.10).png

O objetivo deste artigo é abordar um tema que os desenvolvedores ASP.NET tem alguma dificuldade em alguns momentos onde o projeto começa a ter muitos Controllers. É a nova configuração de rotas para os Controllers ASP.NET. Entenda-se projetos ASP.NET como qualquer ASP.NET, sendo: Web Forms, MVC, WebAPI e SPA (Single Page Application), afinal, hoje em dia estes tipos de projetos são ASP.NET One.

A Microsoft uniu todos os frameworks de ASP.NET, e nomeou como ASP.NET One. Isto possibilita referenciar e usar qualquer coisa que você conheça de ASP.NET nos projetos anteriormente citados.

Os pré-requisitos para este artigo é o Visual Studio .NET 2013, e cabe ressaltar que estou com a versão Preview.

Afinal, o que é uma rota?

Todo projeto de MVC ou Web API tem um Controller, que é o responsável em receber a solicitação do cliente e efetuar o processamento. E, todo Controller tem pelo menos uma Action, que é a ação (método) a ser executado. Para isto, é preciso informar ao projeto qual o caminho, qual a classe e a Action, qual a rota que ele deve encontrar e processar. E, como toda solicitação parte de uma URI, dizemos que o conceito de rota (Route) é exatamente o caminho que a URI deve seguir.

Será que isto só serve para projetos ASP.NET? Claro que não, pois qualquer projeto pode implementar uma requisição a um serviço Web API, por exemplo. Seja uma aplicação Windows 8, Windows Phone, MVC, Console, ASP.NET, WinForms, enfim, basta ter acesso a internet e ao HttpClient.

O Web API 2 e aplicações ASP.NET (MVC 5, Web Forms 5) suportam um novo tipo de rota, fantástica forma de declarar um atributo para as rotas encontrarem as Actions.

Vamos ao projeto de exemplo, o qual criaremos um Web API com algumas Actions e consumiremos no Fiddler, no Browser e numa Console App (de modo assíncrono). O primeiro passo é criar um projeto no Visual Studio 2013, com uma solução em branco chamada ArtigoRotas, o local você pode gravar onde quiser, conforme a figura 1.

Dn423988.75603F834CC1596E22ED2822CE33C0A2(pt-br,MSDN.10).png

Figura 1 – Criação da solução em branco

Clique no botão OK e aguarde o VS criar a solução. Em seguida, no Solution Explorer, clique com o botão direito, selecione Add / New Project. Selecione Web, o template ASP.NET Web Application, o nome do projeto é ServicosWebAPI, conforme a figura 2.

Dn423988.48377D1EDF6676436D5D65DDC05143F2(pt-br,MSDN.10).png

Figura 2 – Criação do projeto ServicosWebAPI

Clique no botão OK e veja que o VS exibe uma tela com os templates do ASP.NET, para que você selecione qual tipo de projeto deseja. Neste caso, selecione Web API, conforme a figura 3, e observe que há dois checkboxes selecionados, MVC e Web API, pois isto é necessário para que o VS crie os devidos templates, instale todas as referências necessárias de DLLs.

Dn423988.2D7B1B886387870CA3DE03669AF798C1(pt-br,MSDN.10).png

Figura 3 – Projeto Web API

Clique no botão Create Project e aguarde o VS criar toda a estrutura do projeto. Como todo projeto MVC e Web API, temos a pasta Models onde deverão ser criadas as classes representando a estrutura de um objeto, cliente, produtos ou pedidos por exemplo. Sendo assim, em Models, clique com o botão direito e selecione Add / Class. Crie a classe Cliente com as seguintes propriedades:

namespace ServicosWebAPI.Models
{
    public class Cliente
    {
        public int ID { get; set; }
        public string Nome { get; set; }
    }
}

Criação do Controller Web API

Antes de criar qualquer controller, recompile toda a solução. Na pasta Controllers já temos o HomeController e o ValuesController, os quais foram criados quando da criação do projeto. O ValuesController é apenas um exemplo de Web API, então, para criar o nosso serviço, clique com o botão direito e selecione Add / Scaffold. Entenda Scaffold como um template apenas. Na janela aberta, selecione o “Scaffold Web API 2 Controller - Empty”, conforme figura 4, a fim de criar o Controller de Web API em branco. Não se preocupe que em seguida iremos inserir todas as operações de GET, POST, PUT e DELETE, também conhecidos como VERBS.

Dn423988.06785066C59A4DE0AB25EB8FF3F8466C(pt-br,MSDN.10).png

Figura 4 – Scaffold de Web API 2

Em seguida forneça os dados, conforme a figura 5. Note que o nome do Controller deve ser ClienteController, não exclua o texto Controller por ser uma convenção do MVC.

Dn423988.B296C593280A3F41943D6946E90BD5C2(pt-br,MSDN.10).png

Figura 5 – Controller de Cliente

Clique no botão Add e aguarde o VS criar o controller. Agora sim, temos o Controller quer será a base do nosso artigo para elencar as rotas. Veja na listagem a seguir, que o controller herda de ApiController. Para que possamos ter alguns dados, adicione uma lista de clientes logo no início da classe. Veja que não usei um banco de dados porque quero focar nas rotas, e não numa requisição de um banco de dados, apesar disso não mudar em nada, basta referenciar o mesmo no modelo do Entity Framework.

public class ClienteController : ApiController
{
    List<Cliente> lista = new List<Cliente> {
    new Cliente { ID=1, Nome= "Microsoft"},
    new Cliente { ID=2, Nome= "Universidade São Paulo"},
    new Cliente { ID=3, Nome= "Petrobrás"},
    new Cliente { ID=4, Nome= "Banco Itaú"},
    new Cliente { ID=5, Nome= "Banco Santander"},
    new Cliente { ID=6, Nome= "Banco Bradesco"},    
   };
}

Vamos preparar os códigos básicos contendo todos os Verbs (GET, Post, Put, Delete) necessários para se fazer a leitura, inclusão, atualização e exclusão de registros via serviço. Em seguida, digite o código para retornar todos os clientes da lista. Veja que esta Action chama-se Get e retorna um IEnumerable de Cliente, ou seja, uma lista com todos os clientes declarados na variável lista. Observe que o comentário é como que a Action deve ser chamada pelo cliente.

// GET api/cliente
public IEnumerable<Cliente> Get()
{
    return lista.ToList();
}

Agora, imagine uma situação em que é preciso retornar apenas um objeto Cliente, o qual foi fornecido o ID como parâmetro de entrada. Veja que usei o SingleOrDefault, mas você poderia usar também o FirstOrDefault ou o Find. Veja que o código usa uma expressão lambda para pesquisar pela propriedade ID igual ao ID do parâmetro.

// GET api/cliente/1
public Cliente Get(int id)
{
    return lista.SingleOrDefault(c => c.ID == id);
}

Action a seguir retorna uma lista de clientes que atenderem o critério, ou seja, retorna todos os clientes onde o nome passado como parâmetro estiver contido na propriedade Nome. Observe que usei o ToLower() para transformar ambos para minúsculos para efeito de comparação.

// GET api/cliente/?nome=teste
public IEnumerable<Cliente> Get(string nome)
{
    return lista.Where(p => p.Nome.ToLower().Contains(nome.ToLower()));
}

A listagem a seguir mostra a Action para atualizar um elemento da lista. Esta é a mesma estrutura se você tivesse uma referência de uma entidade no banco de dados. Eu fiz questão de preparar desta forma, mesmo não tendo um acesso ao banco, mas o código já fica pronto. O PUT recebe o ID e o objeto Cliente montado. Se a estrutura do objeto (ModelState) for válido e o ID do parâmetro for igual ao ID do objeto Cliente, significa que os dados estão corretos, e então, basta localizar o elemento na lista e atualiza-lo. Se tivessemos um Entity Framework, bastaria usar o SaveChanges do contexto. No que dependendo das validações podem retornar erros como BadRequest, NotFound ou OK.

public HttpResponseMessage PutCliente(Int32 id, Cliente cliente)
{
    if (!ModelState.IsValid)
    {
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
    }

    if (id != cliente.ID)
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }

    try
    {
        // update da lista;
        lista.Single(c => c.ID == id);
    }
    catch (Exception ex)
    {
        return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
    }
    return Request.CreateResponse(HttpStatusCode.OK);
}

O POST é usado para incluir um elemento à lista, sendo que o parâmetro é o objeto Cliente. Como usei uma lista fixa, para descobrir o maior ID, usei o Max + 1, para simular um autoincremento do índice. Caso tenha criado com sucesso, é retornado um StatusCode de Create e o objeto Cliente em si.

public HttpResponseMessage PostCliente(Cliente cliente)
{
    if (ModelState.IsValid)
    {
        cliente.ID = lista.Max(c => c.ID) + 1;
        lista.Add(cliente);

        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, cliente);
        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = cliente.ID }));
        return response;
    }
    else
    {
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
    }
}

E para finalizar, temos o Delete que exlcui o elemento a partir do ID passado no parâmetro. O código usa o FirstOrDefault para localizar o cliente a ser excluído e o remove, caso exista.

public HttpResponseMessage DeleteCliente(Int32 id)
{
    Cliente excluir = lista.FirstOrDefault(c => c.ID == id);
    if (excluir == null)
    {
        return Request.CreateResponse(HttpStatusCode.NotFound);
    }

    try
    {
        lista.Remove(excluir);
    }
    catch (Exception ex)
    {
        return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
    }

    return Request.CreateResponse(HttpStatusCode.OK, excluir);
}

Estes códigos anteriores são apenas a base do Controller para que possamos falar sobre as novas rotas.

Requisição das Actions com as Rotas

Recompile a solução e certifique-se que está compilada com sucesso, ou seja, sem erros. Se isto ocorreu, é tudo o que precisamos, sensacional. Agora vamos iniciar as requisições, então, veja a assinatura da primeira Action Get.

public IEnumerable<Cliente> Get()

Como fazer para solicitar esta Action Get? Antes de mais nada é preciso executar o projeto, pressione F5 para roda-lo no browser. Na url digite http://localhost:10392/api/cliente. Veja que o browser IE mostra uma janela com o arquivo JSON carregado, para que você decida se irá abrí-lo ou salvá-lo, conforme a Figura 6.

Dn423988.522869DCA948056932449BF4A5FE1B0F(pt-br,MSDN.10).png

Figura 6 – Requisição do Controller/Action

Clique no botão Abrir e veja que o Notepad mostra exatamente a lista de clientes, conforme a figura 7.

Dn423988.DEA9DCF72B3A19D4D05E790F2C1C197A(pt-br,MSDN.10).png

Figura 7 – Lista de clientes em JSON

Cabe ressaltar que o IE retorna em JSON por default e o Chrome em XML. Para ler apenas um cliente é preciso passar o ID do mesmo, ou seja, com o programa executando, digite o http://localhost:10392/api/cliente/4 para retornar o cliente com ID=4, conforme a figura 8. A partir de agora usarei uma ferrametna gratuita chamada Fiddler, que é perfeita para requisitar e enviar requisições à Web Api.

Dn423988.7F9FD19A245BAAB23EF7CB555D59334C(pt-br,MSDN.10).png

Figura 8 – Retorno do cliente id=4

Já a próxima Action cujo parâmetro é parte do nome do cliente, a solicitação deve ser http://localhost:10392/api/cliente/?nome=banco. O retorno serão todos os clientes que contém o texto banco no nome, conforme figura 9.

Dn423988.521BB4798B29A843B7C408888D796AD0(pt-br,MSDN.10).png

Figura 9 – Retorno de clientes com filtro no nome

Novas declarações de Rotas

O time do ASP.NET implementou uma nova maneira de você declarar uma rota. A rota padrão que você conhece está no arquivo RouteConfig.cs na pasta App_Start, o qual definr um mapeamento de rotas para o projeto. A listagem a seguir mostra o arquivo default criado pelo template do VS, e não iremos alterá-lo em nada. É fato que você pode adicionar quantas rotas forem necessários ao vosso projeto, mas com o aprendizado deste artigo, verá que não é preciso.

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

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

O primeiro passo é alterar o Controller Cliente, onde iremos declarar o Verb (HttpGet, HttpPost, HttpPut e HttpDelete) para cada respectiva operação. Só que isto não é preciso caso você mantenha os nomes padrão como Get, PostCliente, PutCliente e DeleteCliente. Pare pra pensar: isto num projeto nem sempre é inteligível, e num projeto de Web API fica mais claro porque temos estas operações comumente. Já num projeto de MVC e Web Forms, o cenário muda bastante, justamente porque você pode ter dezenas de Controllers. Sendo assim, vamos nomear os Controllers para que fique mais claro.

De acordo com a tabela a seguir, altere apenas as declarações, inserindo o Verb e o novo nome da Action:

DE
// GET api/cliente
public IEnumerable<Cliente> Get()
PARA
[HttpGet("api/cliente/v2")]
public IEnumerable<Cliente> Ler()

DE
// GET api/cliente/1
public Cliente Get(int id)
PARA
[HttpGet("api/cliente/{id:int}")]
public Cliente Ler(int id)

DE
// GET api/cliente/?nome=teste
public IEnumerable<Cliente> Get(string nome)
PARA
[HttpGet("api/cliente/{nome}")]
public IEnumerable<Cliente> Ler(string nome)

Qual a diferença?

Primeiro, alteramos os nomes de Get para Ler. Isto fica mais claro em situações onde há muitas Actions com diferentes parâmetros. Segundo, devido ao nome fora do padrão do MVC (Get, por exemplo), é preciso declarar o VERB, neste caso HTTP. Até aqui, era igual, no entanto, agora veja que além do VERB você pode declarar a rota e o devido parâmetro. Isto deixa a Action muito mais clara.

E, como devo enviar a requisição? Exatamente da mesma forma, nada muda, veja os exemplos:

http://localhost:10392/api/cliente/

http://localhost:10392/api/cliente/2

http://localhost:10392/api/cliente/?nome=banco

Prefixo da Rota

Agora, veja que coisa fantástica que foi implementado na rota. Chama-se prefixo da rota (RoutePrefix), e para isto, você deve declarar quais prefixos você achar devido no atributo do Controller. Sendo assim, adicione três prefixos antes do nome do Controller, sendo:

[RoutePrefix("api/cliente/v2")]
[RoutePrefix("api/clienteBrasil")]
public class ClienteController : ApiController

Mas, por que isto é preciso? Isto é necessário para facilitar a chamada do Controller a partir de qualquer requisição. Cabe ressaltar que isto não é obrigatório, um Controller precisa ter um nome apenas, Cliente por exemplo, mas o fato de permitir adicionar prefixos ao mesmo, nos ajuda a deixar o Controller mais flexível.

Sendo assim, toda e qualquer chamada pode ser feita com as seguintes declarações:

http://localhost:10392/api/cliente

http://localhost:10392/api/cliente/v2

http://localhost:10392/api/clienteBrasil

http://localhost:10392/api/cliente/2

http://localhost:10392/api/cliente/v2/2

http://localhost:10392/api/clienteBrasil/2

http://localhost:10392/api/cliente/?nome=teste

http://localhost:10392/api/cliente/v2/?nome=teste

http://localhost:10392/api/clienteBrasil/?nome=teste

E os parâmetros, o que isto mudam nos códigos, nas declarações das Actions? Agora sim, chegamos ao ponto de deixar as Actions muito mais claras. Altere a declaração das Actions conforme a tabela a seguir:

DE
[HttpGet("api/cliente/v2")]
PARA
[HttpGet]

DE
[HttpGet("api/cliente/{id:int}")]
PARA
[HttpGet("{id:int=1}")]

DE
[HttpGet("api/cliente/{nome}")]
PARA
[HttpGet("{nome}")]

Veja como está tudo muito mais claro e de fácil entendimento. Não que era difícil, eu sei, mas com o uso do prefixo você não precisar alongar a declaração das Actions. A rota permite diversos tipos de parâmetros, e veja a lista a seguir os tipos e como declará-los.

Var

tipo

função

alpha

{x:alpha}

Permite caracteres a-z, tanto maiúsculos quanto minúsculos

bool

{x:bool}

true/false

datetime

{x:datetime}

Valores do tipo data

double

{x:double }

Valores double

int

{x:int}

Valores int

guid

{x:guid}

Valores GUID

lenght

{x:lenght(6)}

String com tamanho específico

lenght

{x:lenght(1,50)}

String com tamanho de 1 a 50

minlength

{x:minlenght(5)}

String com tamanho mínimo de 5

max

{x:max(6)}

Valores inteiros até o tamanho máximo

min

{x:min(4)}

Valores inteiros com um mínimo

range

{x:range(10,100)}

Valores inteiros entre 10 e 100

regex

{x:expressão}

Expressão regular

Veja como você deve alterar as declarações para o PUT, POST e DELETE:

DE
public HttpResponseMessage PutCliente(Int32 id, Cliente cliente)
PARA: sem o RoutePrefix
[HttpPut("api/cliente/v2/{id:int}")]
public HttpResponseMessage Atualizar(Int32 id, Cliente cliente)
PARA: com o RoutePrefix
[HttpPut("{id:int}")]
public HttpResponseMessage Atualizar(Int32 id, Cliente cliente)

DE
public HttpResponseMessage PostCliente(Cliente cliente)
PARA: sem o RoutePrefix
[HttpPost("api/cliente/v2")]
public HttpResponseMessage Adicionar(Cliente cliente)
PARA: com o RoutePrefix
[HttpPost()]
public HttpResponseMessage Adicionar(Cliente cliente)

DE
public HttpResponseMessage DeleteCliente(Int32 id)
PARA: sem o RoutePrefix
[HttpDelete("api/cliente/v2/{id:int}")]
public HttpResponseMessage Excluir(Int32 id)
PARA: com o RoutePrefix
[HttpDelete("{id:int}")]
public HttpResponseMessage Excluir(Int32 id)

Consumindo a Web API

Uma vez construído o serviço, veja como consumir através destas novas rotas declaradas. Para isto, criaremos um projeto de Console Application, afinal, existem muitos exemplos de como consumir e usar um Controller. Neste caso, como o projeto de serviços é de Web API, será um ótimo exemplo de consumo em uma chamada assíncrona.

No solution Explorer, adicione um novo projeto de Console Application chamado ConsoleWebApiArtigo. O primeiro passo é instalar via Nuget o pacote Install-Package Microsoft.AspNet.WebApi.Client.

Veja o código do Program.cs que faz referência ao projeto MVC Web API com os três prefixos de rotas definidos. Claro que você pdoe e deve testar todos.

Este código é assíncrono e basicamente faz uma referência ao HttpClient, sem ele não tem conversa, monta uma requisição e aguarda o retorno da leitura dos dados, para então exibir na tela o ID e o nome de todos os clientes.

//private const string url = "http://localhost:10392/api/cliente";
//private const string url = "http://localhost:10392/api/cliente/v2";
private const string url = "http://localhost:10392/api/clienteBrasil";

static void Main(string[] args)
{
    LerDadosAsync();

    Console.ReadKey();
}

private async static void LerDadosAsync()
{
    using (var client = new HttpClient())
    {
        using (var request = await client.GetAsync(url))
        {
            request.EnsureSuccessStatusCode();
            await request.Content.ReadAsAsync<IEnumerable<Cliente>>()
                    .ContinueWith(t =>
                    {
                        foreach (var p in t.Result)
                        {
                            Console.WriteLine("{0}\t{1}", p.ID, p.Nome);
                        }
                    });
        }
    }
}

class Cliente
{
    public int ID { get; set; }
    public string Nome { get; set; }
}

Execute os dois projetos, sendo que o de Web API primeiro, e depois o de Console. Veja na figura 10 o resultado da leitura de todos os clientes.

Dn423988.61ECEE0B85C4B13B07173DAFA3970E29(pt-br,MSDN.10).png

Figura 10 – Dados dos clientes

Conclusão

A estrutura de rotas é um dos pontos principais a ser entendido numa aplicação ASP.NET. E, no Visual Studio .NET 2013 tudo ficou mais flexível, mais fácil de nomear e definir as rotas, diretamente nos Controllers. Indico que você se dedique aos estudos do ASP.NET MVC, principalmente nesta nova versão 5 que há muitas novidades.

Sobre o Autor

Renato Haddad (rehaddad@msn.com – www.renatohaddad.com ) é MVP, MCT, MCPD e MCTS, palestrante em eventos da Microsoft em diversos países, ministra treinamentos focados em produtividade com o VS.NET 2012/2013, ASP.NET 4/5, ASP.NET MVC, Entity Framework, Reporting Services, Windows Phone e Windows 8. Visite o blog http://weblogs.asp.net/renatohaddad/

Mostrar:
© 2014 Microsoft