ASP.NET MVC 5 - Criando e Consumindo uma Web API - II


Hoje vou continuar a série de artigos que mostra como criar uma WEB API na ASP .NET MVC 5 e depois como consumir essa API em uma aplicação ASP .NET MVC 5.

Continuando a primeira parte do artigo vamos criar o controlador ContatosController na pasta Controllers e definir os métodos Action com as funcionalidades que desejamos expor para a nossa API.

Criando o Controlador ContatosController

Clique com o botão direito do mouse sobre a pasta Controllers e seleciona add -> Controller;

A seguir selecione o template : Web API 2 Controller - Empty e clique em Add;

Informe o nome ContatosController e clique em Add;

Ao final o controlador será criado e você verá um arquivo readme.txt sendo exibido que vai solicitar que você inclua os seguintes namespaces no arquivo Global.asax :

 using System.Web.Http;
 using System.Web.Routing;

E que no método Application_Start deste arquivo você deverá incluir a linha de comando:

 GlobalConfiguration.Configure(WebApiConfig.Register);

Após fazer esses ajustes o seu arquivo Global.asax deve ficar assim:

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace ApiContatos
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            GlobalConfiguration.Configure(WebApiConfig.Register);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}

Aqui é definido o roteamento da API que esta configurando no arquivo WebApiConfig:

using System.Web.Http;
namespace ApiContatos
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

Nesta configuração o acesso a nossa Web API esta definido pela url: http://localhost:XXXX/api/contatos  onde:

Definindo os métodos Action GET

Conforme a convenção padrão da nomenclatura da Web API, um método Action que começa com um "Get" vai manipular uma requisição HTTP GET.

Podemos usar o nome Get ou com qualquer sufixo. Vamos adicionar nosso primeiro método Action Get e denominar GetTodosContatos porque ele retornará todos os contatos do banco de dados.

Seguir uma metodologia de nomeação apropriada aumenta a legibilidade e qualquer pessoa pode entender facilmente a finalidade de um método.

using ApiContatos.Models;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace ApiContatos.Controllers
{
    public class ContatosController : ApiController
    {
        [HttpGet]
        public IHttpActionResult GetTodosContatos()
        {
            IList<Contato> contatos = null;
            using (var ctx = new AppDbContext())
            {
                contatos = ctx.Contatos.Include("Endereco").ToList()
                            .Select(s => new Contato()
                            {
                                ContatoId = s.ContatoId,
                                Nome = s.Nome,
                                Email = s.Email,
                                Telefone = s.Telefone
                            }).ToList();
            }
            if (contatos.Count == 0)
            {
                return NotFound();
            }
            return Ok(contatos);
        }
    }
}

O método Action GetTodosContatos() retorna todos os contatos usando EF.

Se nenhum contato existir no banco de dados, ele retornará a resposta 404 NotFound, caso contrário, ele retornará a resposta 200 OK com os dados dos alunos. Os métodos NotFound() e Ok() definidos na ApiController e retornam as respostas 404 e 200, respectivamente.

Executando o projeto e acioando a Web API : https://localhost:44320/api/contatos ,iremos obter o resultado:



Note que o endereço não esta sendo exibido.

No banco de dados, todo aluno tem pelo menos um ou nenhum endereço. Suponha que você deseje implementar outro método GET para obter todos os Alunos com seus endereços. Se este método não tiver nenhum parâmetro, e for chamado de GetContatosEnderecos(), ao tentar executar você vai obter um erro pois a API não vai saber qual método GET ela deve executar.

Então para contornar esse problema podemos definir um parâmetro opcional chamado incluirEndereco no método GET de forma a retornar todos os contatos ou todos os contatos com seus endereços.

Reescrevendo o método GetTodosContatos temos:

using ApiContatos.Models;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace ApiContatos.Controllers
{
    public class ContatosController : ApiController
    {
        [HttpGet]
        public IHttpActionResult GetTodosContatos(bool incluirEndereco = false)
        {
            IList<Contato> contatos = null;
            using (var ctx = new AppDbContext())
            {
                contatos = ctx.Contatos.Include("Endereco").ToList()
                            .Select(s => new Contato()
                            {
                                ContatoId = s.ContatoId,
                                Nome = s.Nome,
                                Email = s.Email,
                                Telefone = s.Telefone,
                                Endereco = s.Endereco == null || incluirEndereco == false ? null : new Endereco()
                                {
                                    EnderecoId  = s.Endereco.EnderecoId,
                                    Local = s.Endereco.Local,
                                    Cidade = s.Endereco.Cidade,
                                    Estado = s.Endereco.Estado
                                }
                            }).ToList();
            }
            if (contatos.Count == 0)
            {
                return NotFound();
            }
            return Ok(contatos);
        }
    }
}

Agora o método Action GetTodosContatos inclui o parâmetro incluirEndereco com o valor padrão false. Se uma requisição HTTP contiver o parâmetro incluirEndereco na string de consulta com valor true, ele retornará todos os contatos com seu endereço, caso contrário, retornará os contatos sem endereço.

Assim acessando a url : https://localhost:44320/api/contatos?incluirendereco=true teremos o resultado abaixo:

Dessa forma 'matamos dois coelhos com uma só cajadada'.

Seguindo essa abordagem podemos implementar vários métodos GET. Se considerarmos que o controlador de Web API pode incluir vários métodos Get com diferentes parâmetros e tipos podem adicionar outros métodos GET ao nosso controlador, por exemplo:

Com a implementação desses métodos nosso controlador ficou assim:

using ApiContatos.Models;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace ApiContatos.Controllers
{
    public class ContatosController : ApiController
    {
        [HttpGet]
        public IHttpActionResult GetTodosContatos(bool incluirEndereco = false)
        {
            IList<Contato> contatos = null;
            using (var ctx = new AppDbContext())
            {
                contatos = ctx.Contatos.Include("Endereco").ToList()
                            .Select(s => new Contato()
                            {
                                ContatoId = s.ContatoId,
                                Nome = s.Nome,
                                Email = s.Email,
                                Telefone = s.Telefone,
                                Endereco = s.Endereco == null || incluirEndereco == false ? null : new Endereco()
                                {
                                    EnderecoId  = s.Endereco.EnderecoId,
                                    Local = s.Endereco.Local,
                                    Cidade = s.Endereco.Cidade,
                                    Estado = s.Endereco.Estado
                                }
                            }).ToList();
            }
            if (contatos.Count == 0)
            {
                return NotFound();
            }
            return Ok(contatos);
        }
        public IHttpActionResult GetContatoPorId(int? id)
        {
            if (id == null)
                  return BadRequest("O Id do contato é inválido");

            Contato contato = null;
            using (var ctx = new AppDbContext())
            {
                contato = ctx.Contatos.Include("Endereco").ToList()
                         .Where(c => c.ContatoId == id)
                         .Select(c => new Contato()
                         {
                          ContatoId = c.ContatoId,
			Nome = c.Nome,
			Email = c.Email,
			Telefone = c.Telefone,
			Endereco = c.Endereco == null ? null : new Endereco()
			{
			    EnderecoId = c.Endereco.EnderecoId,
			    Local = c.Endereco.Local,
			    Cidade = c.Endereco.Cidade,
			    Estado = c.Endereco.Estado
			}
                         }).FirstOrDefault<Contato>();
            }
            if (contato == null)
            {
                return NotFound();
            }
            return Ok(contato);
        }
        public IHttpActionResult GetContatoPorNome(string nome)
        {
            if (nome == null)
                    return BadRequest("Nome Inválido");

            IList<Contato> students = null;
            using (var ctx = new AppDbContext())
            {
                students = ctx.Contatos.Include("Endereco").ToList()
                    .Where(s => s.Nome.ToLower() == nome.ToLower())
                    .Select(s => new Contato()
                    {
                        ContatoId = s.ContatoId,
                        Nome = s.Nome,
                        Email = s.Email,
                        Telefone = s.Telefone,
                        Endereco = s.Endereco == null ? null : new Endereco()
                        {
                            EnderecoId = s.Endereco.EnderecoId,
                            Local = s.Endereco.Local,
                            Cidade = s.Endereco.Cidade,
                            Estado = s.Endereco.Estado
                        }
                    }).ToList();
            }
            if (students.Count == 0)
            {
                return NotFound();
            }
            return Ok(students);
        }
    }
}

Note que definimos o id como sendo um nullable usando int?; esse recurso permite que tipos por valor sejam tratados como tipos por referência e suportam valores null.

Realizando o teste iremos obter o seguinte resultado:

1-GeContatoPorId - https://localhost:44320/api/contatos?id=5

2- GetContatoPorNome - https://localhost:44320/api/contatos?nome=macoratti

Assim até agora nossa WEB API esta tratando o método HTTP GET onde temos:

HTTP GET Descrição
https://localhost:44320/api/contatos Retorna todos os contatos sem endereço
https://localhost:44320/api/contatos?incluirEndereco=true Retorna todos os contatos com endereço
https://localhost:44320/api/contatos?id=5 Retorna todos os contatos pelo id
https://localhost:44320/api/contatos?nome=macoratti Retorna todos os contatos pelo nome

Fizemos os testes no navegador pois o método GET permite isso, mas podemos testar também no Postman ou mesmo no PowerShell.

Apenas para constar, abrindo uma janela no PowerShell e digitando o comando :

Invoke-RestMethod https://localhost:44320/api/contatos -Method GET

Iremos obter o resultado a seguir:

Testando os métodos HTTP GET no Navegador

Podemos também testar todos os métodos GET no navegador e para isso vamos ajustar o código do arquivo _Layout.cshtml criado no projeto incluindo as linhas de código em azul:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - Contatos</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
            @Html.ActionLink("ApiContatos", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
            <li>@Html.ActionLink("Home", "Index", "Home")</li>
              <li>@Html.ActionLink("Contatos", "contatos", "api")</li>
              <li>@Html.ActionLink("Contatos com Endereço", "contatos", "api",  new { incluirEndereco=true }, null)</li>
             <li>@Html.ActionLink("Contato por Id", "contatos", "api", new { id = 5 }, null)</li>
             <li>@Html.ActionLink("Contato por nome", "contatos", "api", new { nome = "macoratti" }, null)</li>

                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - Macoratti.net</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

Observe que estamos usando uma sobrecarga do método Html.ActionLink para definir a url da API passando os parâmetros. 

Assim, a o código :

<li>@Html.ActionLink("Contato por Id", "contatos", "api", new { id = 5 }, null)</li>

Vai montar a url /api/contatos/id?5

Vamos ajustar a view Index.cshtml do projeto para exibir apenas o contéudo a seguir:

@{
    ViewBag.Title = "Home Page";
}
<div class="jumbotron">
    <h1>API Contatos</h1>
    <p class="lead">API Contatos em Atendimento</p>
</div>

Executando o projeto teremos a página inicial apresentada conforme baixo exibindo os links nos menus:

Agora podemos testar os métodos GET da API clicando nos links do menu da página inicial.

Na próxima parte do artigo vamos criar o método Action HTTP POST para incluir um novo contato.

"E esta é a mensagem que dele(Jesus) ouvimos, e vos anunciamos: que Deus é luz, e não há nele trevas nenhumas."
1 João 1:5

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

Referências:


José Carlos Macoratti