APIs REST com ASP.NET Core 2 e ADO.NET

O objetivo deste artigo é criar APIs REST utilizando ASP.NET Core 2 e ADO.NET, para isso vamos precisar do Visual Studio 2017 que você pode baixar no link abaixo e do SQL Express.

https://www.visualstudio.com/pt-br/downloads/

https://www.microsoft.com/pt-br/sql-server/sql-server-editions-express

A ideia da API, aproveitando o ano de copa do mundo é que possamos criar registros de seleções, baseado em uma seleção vamos criar o Id, o nome da seleção, qual continente ela pertence, o número de participações em copas do mundo e qual foi o melhor resultado em copas.

Para iniciarmos o projeto siga os passos abaixo.

• Clique em File/New Project e selecione .NET Core, ASP.NET Core Web Application e informe o nome do Projeto, conforme (Figura 1).

Figura 1 – Criação do projeto

 

• Na tela seguinte (Figura 2) selecione ASP.NET Core 2.0, Web API e clique em OK

Figura 2 – Novo projeto ASP.NET Core Web Application

 

• Após estas duas etapas inicias o Visual Studio irá criar o projeto em seguida vamos adicionar a nossa primeira classe. Clique com o botão direito em cima do projeto (AspNetWebApi) e selecione Add/New Folder, nomeie esta nova pasta chamando-a de Models, em seguida clique com o botão direito do mouse em cima da nova pasta e selecione Add/Class. Em Code selecione Class, em Name informe Selecao conforme (Figura 3).

 

Figura 3 – Criação da classe Selecao

 

• Em seguida vamos criar as propriedades da classe Selecao, digite ou copie e cole o código abaixo.

namespace AspNetWebApi.Models
{
public class Selecao{

public int Id { get; set; }

public string NomeSelecao { get; set; }

public string Continente { get; set; }

public int NumeroParticipacoes { get; set; }

public string MelhorResultado { get; set; }
}
}

• Nosso próximo passo é incluir uma Interface chamada ISelecao, para isso clique com o botão direito do mouse em cima da pasta Models e selecione Add/New Item, em Code selecione Interface e em Name digite ISelecao. (Figura 4)

 

Figura 4 – Interface ISelecao.cs

 

• Digite ou copie e cole a interface ISelecaoDal, quem contém todos os métodos que utilizaremos na classe Dal, os métodos contemplam obter todas as seleções, excluir, incluir, atualizar e obter por id. (CRUD).


using System.Collections.Generic;

namespace WebApiCore2.Models
{
public interface ISelecaoDal
{
IEnumerable<Selecao> ObterSelecoes();
int IncluirSelecao(Selecao selecao);
int AtualizarSelecao(Selecao selecao);
Selecao ObterSelecaoPorId(int id);
int ExcluirSelecao(int id);
}
}

 

• Após a criação da Interface vamos criar a classe SelecaoDal, ela irá herdar da Interface e num próximo passo vamos codificar os métodos CRUD. Clique com o botão direito do mouse na pasta Models e selecione Add/New Item, em seguida em Code e Class e digite em Name SelecaoDal, conforme (Figura 5).

Figura 5 – SelecaoDal.cs

 

• Após a criação da classe SelecaoDal vamos herdar da Interface ISelecao, após o nome da classe SelecaoDal digite dois pontos e ISelecaoDal, irá aparecer uma lâmpada (Figura 6), clique nela e selecione Implement Interface, esta ação irá criar todos os métodos na classe, porém não implementados. (Figura 7)

Figura 6 – Herança

 

Figura 7 – Métodos não implementados

 

• Nosso próximo passo será criar o construtor da classe SelecaoDal, primeiro criamos um field chamado _connectionString onde iremos utilizar em todos os métodos e em seguida o construtor, conforme código abaixo.

readonly string _connectionString;

public SelecaoDal(IConfiguration config)
{
_connectionString = config.GetConnectionString("DefaultConnection");
}

Observação – IConfiguration utiliza a namespace Microsoft.Extensions.Configuration. (Figura 8)

Figura 8 – IConfiguration

• Antes de explicar a configuração na classe Startup.cs vou colocar o link do site da Microsoft.
https://docs.microsoft.com/pt-br/aspnet/core/fundamentals/startup?view=aspnetcore-2.1

Figura 9- ConfigureServices

 

Quando implementarmos o Controller, por exemplo o Get e chamarmos da Dal o método _selecaoDao.ObterSelecoes(); não vamos precisar criar um new como por exemplo

SelecaoDal dal = new SelecaoDal();
dal.ObterSelecoes();

E este foi o motivo de termos criado a Interface ISelecao, porque através de injeção de dependência temos acesso aos métodos da dal, e de forma nativa no .NET Core, desta maneira não criamos dependência da classe Controller.

• Abra a classe Startup.cs que fica na raiz do projeto e em ConfiguraServices adicione o serviço AddTransient que implementa ISelecaoDal e SelecaoDal e referencie a models (Figura 10)

Figura 10 – ConfigureServices

 

• Agora vamos adicionar a string de conexão, abra o arquivo appsettings.json que fica na raiz do projeto e antes de “Logging” digite a string conforme código abaixo, porém com os dados do seu banco.

"ConnectionStrings": {
"DefaultConnection": "Data Source=dadosdosseubanco;Initial Catalog=AprendaDotNet;Persist Security Info=True;User ID=sa;Password=suasenha;"
},

• Como vamos utilizar ADO.NET vou criar procedures para os CRUDs, “Por que você não utiliza o Entity?” Porque a maioria dos artigos são feitos com Entity, eu quis mostrar que existem outras formas de acessar dos com .NET.  O script da criação das procs e do banco, estão junto com os fontes que estão disponíveis no link no final do artigo

• Com o banco e as procedures criadas vamos codificar a classe SelecaoDal
Referencie as namespaces

using System.Data;
using System.Data.SqlClient;

 

1. O primeiro método é o IncluirSelecao que recebe como argumento um objeto Selecao, em seguida informamos a conexão informamos qual procedure vamos utilizar e informa os parâmetros da procedure IncluirSelecao.

 

//Inclusao de uma nova selecao
public int IncluirSelecao(Selecao selecao)
{
try
{
using (var con = new SqlConnection(_connectionString))
{
var cmd = new SqlCommand("IncluirSelecao", con)
{
CommandType = CommandType.StoredProcedure
};

cmd.Parameters.AddWithValue("@Selecao", selecao.NomeSelecao);
cmd.Parameters.AddWithValue("@Continente", selecao.Continente);
cmd.Parameters.AddWithValue("@NumeroParticipacoes", selecao.NumeroParticipacoes);
cmd.Parameters.AddWithValue("@MelhorResultado", selecao.MelhorResultado);

con.Open();
return cmd.ExecuteNonQuery();
}
}
catch (Exception)
{
return 0;
}

}

 

2. O segundo método é o AtualizarSelecao, ele é bem parecido com o Incluir.

 

//Atualizar uma selecao
public int AtualizarSelecao(Selecao selecao)
{

using (var con = new SqlConnection(_connectionString))
{
var cmd = new SqlCommand("AtualizarSelecao", con)
{
CommandType = CommandType.StoredProcedure
};

cmd.Parameters.AddWithValue("@Id", selecao.Id);
cmd.Parameters.AddWithValue("@Selecao", selecao.NomeSelecao);
cmd.Parameters.AddWithValue("@Continente", selecao.Continente);
cmd.Parameters.AddWithValue("@NumeroParticipacoes", selecao.NumeroParticipacoes);
cmd.Parameters.AddWithValue("@MelhorResultado", selecao.MelhorResultado);
con.Open();
return cmd.ExecuteNonQuery();
}

}

3. Abaixo temos a exclusão das seleções.

//Excluir selecao por id
public int ExcluirSelecao(int id)
{
using (var con = new SqlConnection(_connectionString))
{
var cmd = new SqlCommand("ExcluirSelecao", con)
{
CommandType = CommandType.StoredProcedure
};

cmd.Parameters.AddWithValue("@Id", id);

con.Open();
return cmd.ExecuteNonQuery();
}

}

4. O método ObterSelecaoPorId recebe como parâmetro o id da seleção e é o único método que não utilizamos procedure, e para isso realizamos a query diretamente no método, em seguida verificamos se a consulta tem resultados (HasRows), caso positivo preenchemos um objeto Selecao e retornamos.

 

//Obter selecao por id
public Selecao ObterSelecaoPorId(int id)
{
var selecao = new Selecao();

using (var con = new SqlConnection(_connectionString))
{
var query =
$"SELECT Id, Selecao, Continente, NumeroParticipacoes, MelhorResultado FROM SelecoesCopa2018 WHERE Id = {id}";
var cmd = new SqlCommand(query, con);

con.Open();
var reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
selecao.Id = Convert.ToInt32(reader["Id"]);
selecao.NomeSelecao = reader["Selecao"].ToString();
selecao.Continente = reader["Continente"].ToString();
selecao.NumeroParticipacoes = Convert.ToInt32(reader["NumeroParticipacoes"]);
selecao.MelhorResultado = reader["MelhorResultado"].ToString();
}
}
else
return null;
}
return selecao;
}

5. Por fim o método que retorna todas as seleções.

//Método que obtem todos as selecoes
public IEnumerable<Selecao> ObterSelecoes()
{
var selecaosList = new List<Selecao>();
using (var con = new SqlConnection(_connectionString))
{
var cmd = new SqlCommand("ObterTodasAsSelecoes", con)
{
CommandType = CommandType.StoredProcedure
};

con.Open();
var reader = cmd.ExecuteReader();

while (reader.Read())
{
var employee = new Selecao
{
Id = Convert.ToInt32(reader["Id"]),
NomeSelecao = reader["Selecao"].ToString(),
Continente = reader["Continente"].ToString(),
MelhorResultado = reader["MelhorResultado"].ToString(),
NumeroParticipacoes = Convert.ToInt32(reader["NumeroParticipacoes"])
};

selecaosList.Add(employee);
}
con.Close();
}
return selecaosList;
}

• Continuando o nosso projeto vamos adicionar a SelecaoController, clique com o botão direito do mouse na pasta Controllers em selecione Add/Controller e em Add Scaffold selecione API Controller – Empty e clique em Add (Figura 11), em Controller name digite SelecaoController.

Figura 11 – API Controller

 

• Na (Figura 12) temos como vai ser a nossa API Controller. Perceba os verbos que serão utilizados, temos o Get no qual chamará o obterselecoes que nos retornará todas as seleções cadastradas, o post que irá criar a seleção, Get(id) que retorna a seleção por id, o Put que atualiza uma seleção e por fim o Delete que exclui uma seleção, mais abaixo vamos explicar um por um.

 

Figura 12- SelecaoController

 

• Inicialmente vamos criar um field que aponta para a interface ISelecaoDal de nome _selecaoDao;

private readonly ISelecaoDal _selecaoDal;

• Em seguida vamos criar o construtor da Controller, agora podemos explicar melhor como funciona a injeção de dependência, mais abaixo vamos chamar _selecaoDao.ObterSelecoes(); perceba que sem utilizarmos o new selecaoDao, ou seja criar um objeto SelecaoDal podemos ter acessos aos métodos que estão na classe através da Interface, ou seja nossa Controller está desacoplada da classe dal

public SelecaoController(ISelecaoDal selecaoDal)
{
_selecaoDal = selecaoDal;
}

 

Figura 13- Namespace Models

 

• A primeira Action é a Get, ela utiliza o método obterselecoes, e em seguida retorna um OK que é um status code que significa que foi tudo OK (Figura 14) com a consulta, é muito importante você retornar os verbos corretos para as respectivas chamadas na API e abaixo vamos comentar todos os verbos.

[HttpGet]
public IActionResult Get()
{
var result = _selecaoDal.ObterSelecoes();
return Ok(result);

}

 

Figura 14 – Status OK – 200

 

• A segunda Action é a Create, inicialmente decoramos com HttpPost informando que esta Action é um post, em seguida criamos uma rota chamada criarselecao, com isso facilitamos a chamada da action, vamos ver isso mais abaixo no Postman

[HttpPost, Route("criarselecao")]
public IActionResult Create([FromBody] Selecao selecao)
{
if (selecao == null)
return BadRequest();

var status = _selecaoDal.IncluirSelecao(selecao);

if (status != 1)
return StatusCode(500, "Erro ao incluir a selecao");

return Ok();
}

• A próxima Action é Obter Selecao por Id, recebemos como parâmetro o id da seleção e retornamos um OK com o respectivo objeto, alguns detalhes que não podem ficar despercebidos nesta Action, perceba que caso uma seleção não seja encontrado será retornado o verto NotFound, que tem o código 404, ou seja não é um erro, e sim um status informando que o busca não foi encontrada e outro detalhe que deve perceber é a rota, alteramos para receber como parâmetro o id e mudamos o nome de Get para obterselecaoporid.

 

[HttpGet("{id}/obterselecaoporid")]
public IActionResult Get(int id)
{
var selecao = _selecaoDal.ObterSelecaoPorId(id);

if (selecao == null)
return NotFound();

return Ok(selecao);
}

• Em seguida vamos criar a Action que atualiza uma seleção. Perceba o atributo [FromBody] quando vamos receber como parâmetro uma Selecao é necessário informa-lo pois estamos recebendo como argundo um tipo complexo, para um tipo simples como int, string não é necessário. Caso ocorro um erro ao atualizar utilizamos StatusCode com o código 500 e por fim um novo verbo que é padrão para o put, que é NoContent que retorna o código 204

Esta é melhor explicação que encontrei na Web

Quando o ASP.NET Web API chama um método em um Controller, ele deve definir valores para os parâmetros, um processo chamado binding de parâmetro.

Por padrão, a API da Web usa as seguintes regras para vincular parâmetros:

Se o parâmetro for um tipo “simples” , a API da Web tentará obter o valor do URI . Os tipos simples incluem os tipos primitivos do .NET (int, bool, double e assim por diante), além de TimeSpan, DateTime, Guid, decimal e string, além de qualquer tipo com um conversor de tipos que possa converter de uma string.

Para tipos complexos, a API da Web tenta ler o valor do corpo da mensagem, usando um formatador do tipo de mídia.

Portanto, se você quiser substituir o comportamento padrão acima e forçar a API da Web a ler um tipo complexo do URI, adicione o [FromUri]atributo ao parâmetro. Para forçar a API da Web a ler um tipo simples do corpo da solicitação, adicione o [FromBody]atributo ao parâmetro.

Portanto, para responder à sua pergunta, a necessidade dos atributos [FromBody]e [FromUri]da API da Web é simplesmente substituir, se necessário, o comportamento padrão, conforme descrito acima. Observe que você pode usar os dois atributos para um método do controlador, mas apenas para parâmetros diferentes.

Há muito mais informações na web se você usar o google “web api parameter binding”.

 

[HttpPut, Route("atualizarselecao")]
public IActionResult Put([FromBody] Selecao selecao)
{
if (selecao == null)
return BadRequest();

var status = _selecaoDal.AtualizarSelecao(selecao);

if (status != 1)
return StatusCode(500, "Erro ao atualizar a selecao");

return NoContent();
}

• Por fim vamos realizar a exclusão da seleção, repare no atributo HttpDelete e na rota, excluir seleção, o statuscode 500 também é utilizado e NoContent caso tudo ocorra como o esperado.

[HttpDelete("{id}/excluirselecao")]
public IActionResult Delete(int id)
{
var status = _selecaoDal.ExcluirSelecao(id);

if (status != 1)
return StatusCode(500, "Erro ao excluir a selecao");

return NoContent();
}

• Vamos fazer a última modificação em nosso projeto, através do Solution Explorer abra o arquivo launchSettings.json que encontra-se em Properties e altere os valores de applicationUrl e launchUrl, desta forma padronizamos a porta para 5000 e informar que a Selecao Controller será a inicial.

Figura 15 – launchSettings.json

 

Acabamos de criar a Web API utilizando verbos e retornando os status que são padrão em uma API REST, em seguida vamos testar utilizando a ferramenta Postman que você pode baixar utilizando a URL abaixo.

https://www.getpostman.com/

Vou usar a seguinte tabela para nos ajudar nos testes.

 

Rússia (Europa/país-sede)

11ª participação (incluindo a União Soviética)

Melhor resultado: 4° lugar (1966)

Última participação e resultado: 2014 (eliminada na primeira fase)

Brasil (América do Sul)

21ª participação

Melhor resultado: Campeã (1958, 1962, 1970, 1994 e 2002)

Última participação e resultado: 2014 (4ª colocada)

Irã (Ásia)

5ª participação

Melhor resultado: Primeira fase (1978, 1998, 2006 e 2014)

Última participação e resultado: 2014 (eliminada na primeira fase)

Japão (Ásia)

6ª participação

Melhor resultado: Oitavas de final (2002 e 2010)

Última participação e resultado: 2014 (eliminada na primeira fase)

México (América Central e do Norte)

16ª participação

Melhor resultado: Quartas de final (1970 e 1986)

Última participação e resultado: 2014 (eliminada nas oitavas de final)

Bélgica (Europa)

13ª participação

Melhor resultado: 4° lugar (1986)

Última participação e resultado: 2014 (eliminada nas quartas de final)

Coreia do Sul (Ásia)

10ª participação

Melhor resultado: 4° lugar (2002)

Última participação e resultado: 2014 (eliminada nas quartas de final)

Arábia Saudita (Ásia)

5ª participação

Melhor resultado: Oitavas de final (1994)

Última participação e resultado: 2006 (eliminada na primeira fase)

 

Vou deixar o arquivo json que você pode importar, clique em File/Import

• Vamos iniciar criando uma seleção (Figura 16). Perceba que informamos um json com os campos e a informações quer desejamos cadastrar.

[HttpPost, Route("criarselecao")]
public IActionResult Create([FromBody] Selecao selecao)

 

Figura 16 – Post – Criar seleção

 

• Em seguida vamos listar todas a seleções (Figura 17)

[HttpGet]
public IActionResult Get()
Figura 17- Get

 

• Exclusão da seleção (Figura 18), perceba o parâmetro na url.

Figura 18 – Excluir seleção

 

• Obter por id (Figura 19)

Figura 19 – Obter por id

 

• Atualizar seleção

[HttpPut, Route("atualizarselecao")]
public IActionResult Put([FromBody] Selecao selecao)
Figura 20 – Atualizar seleção

Os fontes do projeto estão disponíveis no GitHub através do link abaixo.
Fontes: https://github.com/fabiogalante/AspNetWebApi

Dúvidas e sugestões
fabiogalantemans@aprendadotnet.com.br