ASP .NET Core  - Consumindo uma Web API com React - II


  Neste artigo vamos continuar criando a Web API ASP .NET Core para depois consumi-la usando uma aplicação React.

Continuando o artigo anterior vamos agora criar o serviço e o controlador para gerenciar informações dos alunos.

Criando um serviço para alunos

Vamos agora iniciar a criação dos serviços e para fazer isso poderíamos apenas criar uma classe concreta e definir nela os serviços que vamos usar e a seguir registrar esta classe no contêiner de injeção de dependência, mas a forma mais correta é primeiro definir uma interface e a seguir implementar essa interface na classe concreta e é isso que vamos fazer.

Vamos criar na pasta Services uma interface chamada AlunoService e a seguir vamos definir o seguinte código:

using AlunosApi.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace AlunosApi.Services
{
    public interface IAlunoService
    {
        Task<IEnumerable<Aluno>> GetAlunos();
        Task<Aluno> GetAluno(int id);
        Task<IEnumerable<Aluno>> GetAlunoByNome(string nome);
        Task CreateAluno(Aluno aluno);
        Task UpdateAluno(Aluno aluno);
        Task DeleteAluno(Aluno aluno);
    }
}

A seguir vamos criar na mesma pasta a classe AlunosService onde vamos definir implementar esta interface e definir os métodos para acessar e persistir os dados dos alunos no SQL Server usando uma instância da classe de contexto: AppDbContext.

Geralmente se costuma implementar o padrão repositório criando uma interface e uma classe concreta mas para simplificar eu vou criar apenas uma classe onde vou injetar a instância do contexto: (Além disso a classe DbContext é uma combinação dos padrões Unit Of Work e Repository.)

using AlunosApi.Context;
using AlunosApi.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AlunosApi.Services
{
    public class AlunosService : IAlunoService
    {
        private readonly AppDbContext _context;

        public AlunosService(AppDbContext context)
        {
            _context = context;
        }

        public async Task<IEnumerable<Aluno>> GetAlunos()
        {
            try
            {
                return await _context.Alunos.ToListAsync();
            }
            catch
            {
                throw;
            }
        }

        public async Task CreateAluno(Aluno aluno)
        {
            _context.Alunos.Add(aluno);
            await _context.SaveChangesAsync();
        }

        public async Task UpdateAluno(Aluno aluno)
        {
            _context.Entry(aluno).State = EntityState.Modified;
            await _context.SaveChangesAsync();
        }

        public async Task<Aluno> GetAluno(int id)
        {
            Aluno aluno = await _context.Alunos.FindAsync(id);
            return aluno;
        }

        public async Task DeleteAluno(Aluno aluno)
        {
            _context.Alunos.Remove(aluno);
            await _context.SaveChangesAsync();
        }

        public async Task<IEnumerable<Aluno>> GetAlunoByNome(string nome)
        {
            if (!string.IsNullOrWhiteSpace(nome))
            {
                var alunos = await _context.Alunos.Where(n => n.Nome.Contains(nome)).ToListAsync();
                return alunos;
            }
            else
            {
                var alunos = await GetAlunos();
                return alunos;
            }
        }
    }
}

Nesta classe de serviço criamos os seguintes métodos assíncronos usando os recursos de async/await e Task:

  1. GetAlunos - Retorna os alunos cadastrados;
  2. GetAluno - Retorna um aluno pelo seu Id;
  3. CreateAluno - Cria um novo aluno;
  4. UpdateAluno - Atualiza um aluno existente;
  5. DeleteAluno - Deleta um aluno pelo seu id;
  6. GetAlunoByNome - Retorna os alunos cujos nomes atendem o critério da consulta;

Vamos agora usar esta classe no controlador AlunosController que iremos criar na pasta Controllers.

Na classe AlunosController vamos injetar uma instância da classe AlunosService e para isso devemos registrar este serviço no método ConfigureServices da classe Startup:

  public void ConfigureServices(IServiceCollection services)
   {
          services.AddControllers();

         services.AddDbContext<AppDbContext>(options =>
           options.
UseSqlServer(Configuration.GetConnectionString
                                 ("
DefaultConnection")));

      
  services.AddScoped<IAlunoService, AlunosService>();

        services.AddSwaggerGen(c =>
        {
           c.SwaggerDoc("v1", new OpenApiInfo
                   { Title = "Alunos.API", Version = "v1" });

        });
  }

Criando o controlador AlunosController

Na pasta Controllers vamos criar o controlador AlunosController:

using AlunosApi.Models;
using AlunosApi.Services;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace AlunosApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]

    public class AlunosController : ControllerBase
    {
        private readonly IAlunoService _alunoService;

        public AlunosController(IAlunoService alunoService)
        {
            _alunoService = alunoService;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<Aluno>>> Index()
        {
            try
            {
                var alunos = await _alunoService.GetAlunos();
                return Ok(alunos);
            }
            catch
            {
                //return BadRequest("Request inválido");
                return StatusCode(StatusCodes.Status500InternalServerError,
                          "Erro ao acessar dados de alunos");
            }
        }

        [HttpGet("{id:int}", Name = "GetAluno")]
        public async Task<ActionResult<Aluno>> Details(int id)
        {
            var aluno = await _alunoService.GetAluno(id);

            if (aluno == null)
                return NotFound($"Aluno com id= {id} não encontrado");

            return Ok(aluno);
        }

        [HttpPost]
        public async Task<ActionResult> Create(Aluno aluno)
        {
            try
            {
                await _alunoService.CreateAluno(aluno);
                return CreatedAtRoute("GetAluno", new { id=aluno.Id }, aluno);
            }
            catch
            {
                return BadRequest("Request inválido");
            }
        }

        [HttpGet("AlunosPorNome")]
        public async Task<ActionResult<IEnumerable<Aluno>>>
                              GetAlunoPorNome([FromQuery] string nome)
        {
            var alunos = await _alunoService.GetAlunoByName(nome);

            if (alunos == null)
                return NotFound($"Não existem alunos com nome = {nome}");

            return Ok(alunos);
        }    

        [HttpPut("{id:int}")]
        public async Task<ActionResult> Edit(int id, [FromBody] Aluno aluno)
        {
            try
            {
                if (aluno.Id == id)
                {
                    await _alunoService.UpdateAluno(aluno);
                    return CreatedAtRoute("GetAluno", new { id = aluno.Id }, aluno);
                }
                else
                {
                    return BadRequest("Dados inválidos");
                }
            }
            catch (Exception)
            {
                return BadRequest("Request inválido");
            }
        }

        [HttpDelete("{id:int}")]
        public async Task<ActionResult> Delete(int id)
        {
            try
            {
                var aluno = await _alunoService.GetAluno(id);
                if (aluno != null)
                {
                    await _alunoService.DeleteAluno(aluno);
                    return Ok(aluno);
                }
                else
                {
                    return NotFound($"Aluno com id= {id} não encontrado");
                }
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }
    }
}

Neste código temos a implementação da nossa API usando os verbos HTTP e mapeando os métodos para expor os endpoints da nossa API. Assim definimos:

Os verbos HTTP usados em cada request descrevem o que fazer com o recurso. No nosso exemplo estamos realizando um CRUD.

Nota: No Bloco try/catch podemos retornar BadRequest ou outro código de status como 500 InternalServerError.

Além disso no request e response usados temos os seguintes participantes:

  1. Request Header - Quando um cliente envia uma solicitação ao servidor, a solicitação contém um cabeçalho e um corpo. O método de solicitação contém informações adicionais, como - que tipo de resposta é necessária.
  2. Request Body - Contém os dados que desejamos enviar ao servidor;
  3. Responde Body - Contém os dados com a resposta do servidor;
  4. Responde Status Codes - Esses são os códigos de status HTTP que fornecem ao cliente detalhes sobre o status da solicitação. Alguns dos códigos de status comuns são 404 não encontrado, 204 Sem conteúdo e assim por diante.

Com isso temos todos os endpoints da API definidos e podemos testar usando a interface do Swagger ou do Postman.

Habiliando o CORS

Antes de prosseguir precisamos realizar a última configuração na API que é habilitar o CORS.

A sigla CORS ou Cross-Origin Resource Sharing ou 'compartilhamento de recursos de origem cruzada' é um padrão W3C sendo uma especificação de uma tecnologia de navegadores que define meios para um servidor permitir que seus recursos sejam acessados por uma página web de um domínio diferente.

Usando o CORS, um servidor pode permitir algumas solicitações de origem cruzada (domínio) e rejeitar outras. E para permitir que o nosso projeto React acesse a nossa API precisamos habilitar o CORS no projeto API.

Para fazer isso precisamos adicionar o grupo de políticas no método ConfigureServices usando o método AddCors, e, depois selecionar a política desejada no método Configure da classe Startup.

Assim no método ConfigureServices da classe Startup vamos incluir o código abaixo:

public void ConfigureServices(IServiceCollection services)
{
            services.AddControllers();

            services.AddDbContext<AppDbContext>(options =>
                options.UseSqlServer(Configuration
                         .GetConnectionString("DefaultConnection")));

            services.AddScoped<IAlunoService, AlunosService>();           

            services.AddCors();

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Alunos.API", Version = "v1" });
            });
 }

Este código instala o middleware para o CORS incluindo-o no pipeline.

E a seguir no método Configure da mesma classe vamos incluir o código a seguir:

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
 {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json",
                                                 "AlunosApi v1"));
            }

            app.UseCors(options =>
            {
                options.WithOrigins("http://localhost:3000");
                options.AllowAnyMethod();
                options.AllowAnyHeader();
            });

            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
 }

No código usado estamos habilitando o CORS e definindo que serão permitidas requisição com origem em 'http://localhost:3000' que é a Url a partir de onde a nossa aplicação React vai enviar as requisições e também estamos permitindo qualquer método e qualquer Header.

Com isso nossa API poderá atender as requisições feitas pela nossa aplicação React.

Na próxima parte do artigo iremos testar a nossa API.

"Os meus olhos anteciparam as vigílias da noite, para meditar na tua palavra."
Salmos 119:148

Referências:


José Carlos Macoratti