Introdução

No mundo do desenvolvimento de software, a criação de APIs RESTful se tornou uma prática essencial, permitindo que diferentes aplicações se comuniquem de forma eficiente. Neste tutorial, vamos explorar o processo de desenvolvimento de uma API RESTful utilizando C# e ASP.NET Core. Vamos usar o Swagger para fornecer documentação automática, facilitando a interação com a API e permitindo que os desenvolvedores entendam rapidamente como utilizá-la. Também utilizaremos o AutoMapper, uma biblioteca que simplifica o mapeamento entre diferentes objetos, o que é extremamente útil quando trabalhamos com modelos de dados e DTOs (Data Transfer Objects). Por meio deste guia prático, você adquirirá conhecimento passo a passo sobre como criar uma API robusta, efetuar o mapeamento de dados e garantir que a documentação esteja sempre atualizada, proporcionando uma experiência perfeita tanto para os desenvolvedores quanto para os usuários finais.

Etapas

  1. Configuração do Ambiente de Desenvolvimento

    Para começar, você deve ter o .NET SDK instalado. Você pode baixá-lo no site oficial da Microsoft. Após a instalação, verifique se tudo está instalado corretamente usando o seguinte comando no terminal:

    commands
    # Verificar se o .NET SDK está instalado
    dotnet --version

  2. Criação do Projeto ASP.NET Core

    Usando o terminal, navegue até o diretório onde deseja criar o projeto e execute o seguinte comando para criar uma nova API ASP.NET Core:

    commands
    # Criar um novo projeto API
    dotnet new webapi -n MyApi
    cd MyApi

  3. Instalação do Swagger

    Para adicionar o Swagger ao seu projeto, abra o arquivo `MyApi.csproj` e adicione a dependência do Swagger. Alternativamente, você pode usar o comando a seguir:

    commands
    dotnet add package Swashbuckle.AspNetCore

  4. Configuração do Swagger no `Startup.cs`

    Para habilitar o Swagger, você precisa modificar a classe `Startup`. No método `ConfigureServices`, adicione a linha para adicionar os serviços do Swagger. Em seguida, no método `Configure`, adicione a middleware do Swagger:

    Startup.cs
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "MyApi", Version = "v1" });
        });
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    
        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyApi v1");
        });
    }

  5. Criação do Modelo `Produto`

    Crie uma pasta chamada `Models` e dentro dela, crie uma classe `Produto` que represente o modelo de dados:

    Produto.cs
    namespace MyApi.Models
    {
        public class Produto
        {
            public int Id { get; set; }
            public string Nome { get; set; }
            public decimal Preco { get; set; }
        }
    }

  6. Criação do Repositório `IProdutoRepository`

    Crie uma interface `IProdutoRepository` na pasta `Repositories` que definirá os métodos CRUD:

    IProdutoRepository.cs
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using MyApi.Models;
    
    namespace MyApi.Repositories
    {
        public interface IProdutoRepository
        {
            Task<IEnumerable<Produto>> GetAllProdutosAsync();
            Task<Produto> GetProdutoByIdAsync(int id);
            Task<Produto> CreateProdutoAsync(Produto produto);
            Task UpdateProdutoAsync(Produto produto);
            Task DeleteProdutoAsync(int id);
        }
    }

  7. Implementação do Repositório `ProdutoRepository`

    Implemente a classe `ProdutoRepository`, que implementa a interface criada anteriormente:

    ProdutoRepository.cs
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using MyApi.Models;
    
    namespace MyApi.Repositories
    {
        public class ProdutoRepository : IProdutoRepository
        {
            private static List<Produto> produtos = new List<Produto>();
    
            public async Task<IEnumerable<Produto>> GetAllProdutosAsync()
            {
                return await Task.FromResult(produtos);
            }
    
            public async Task<Produto> GetProdutoByIdAsync(int id)
            {
                var produto = produtos.Find(p => p.Id == id);
                return await Task.FromResult(produto);
            }
    
            public async Task<Produto> CreateProdutoAsync(Produto produto)
            {
                produto.Id = produtos.Count + 1;
                produtos.Add(produto);
                return await Task.FromResult(produto);
            }
    
            public async Task UpdateProdutoAsync(Produto produto)
            {
                var existingProduto = produtos.Find(p => p.Id == produto.Id);
                if (existingProduto != null)
                {
                    existingProduto.Nome = produto.Nome;
                    existingProduto.Preco = produto.Preco;
                }
            }
    
            public async Task DeleteProdutoAsync(int id)
            {
                var produto = produtos.Find(p => p.Id == id);
                if (produto != null)
                {
                    produtos.Remove(produto);
                }
            }
        }
    }

  8. Criação do Controlador `ProdutoController`

    Crie uma classe chamada `ProdutoController` na pasta `Controllers`, que utilizará o repositório para expor os endpoints da API:

    ProdutoController.cs
    using Microsoft.AspNetCore.Mvc;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using MyApi.Models;
    using MyApi.Repositories;
    
    namespace MyApi.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class ProdutoController : ControllerBase
        {
            private readonly IProdutoRepository _produtoRepository;
    
            public ProdutoController(IProdutoRepository produtoRepository)
            {
                _produtoRepository = produtoRepository;
            }
    
            [HttpGet]
            public async Task<ActionResult<IEnumerable<Produto>>> GetAllProdutos()
            {
                return Ok(await _produtoRepository.GetAllProdutosAsync());
            }
    
            [HttpGet("{id}")]
            public async Task<ActionResult<Produto>> GetProduto(int id)
            {
                var produto = await _produtoRepository.GetProdutoByIdAsync(id);
                if (produto == null)
                    return NotFound();
                return Ok(produto);
            }
    
            [HttpPost]
            public async Task<ActionResult<Produto>> CreateProduto(Produto produto)
            {
                var createdProduto = await _produtoRepository.CreateProdutoAsync(produto);
                return CreatedAtAction(nameof(GetProduto), new { id = createdProduto.Id }, createdProduto);
            }
    
            [HttpPut]
            public async Task<IActionResult> UpdateProduto(int id, Produto produto)
            {
                if (id != produto.Id)
                    return BadRequest();
                await _produtoRepository.UpdateProdutoAsync(produto);
                return NoContent();
            }
    
            [HttpDelete("{id}")]
            public async Task<IActionResult> DeleteProduto(int id)
            {
                await _produtoRepository.DeleteProdutoAsync(id);
                return NoContent();
            }
        }
    }

  9. Adicionando o AutoMapper para Mapeamento de Objetos

    Para simplificar o mapeamento de objetos, adicione a biblioteca AutoMapper ao seu projeto com o seguinte comando:

    commands
    dotnet add package AutoMapper
    dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection

  10. Configuração do AutoMapper

    Crie uma classe de perfil para configurar os mapeamentos entre as entidades e os DTOs. Crie uma pasta chamada `Profiles` e dentro dela, crie a classe `MappingProfile`:

    MappingProfile.cs
    using AutoMapper;
    using MyApi.Models;
    
    namespace MyApi.Profiles
    {
        public class MappingProfile : Profile
        {
            public MappingProfile()
            {
                CreateMap<Produto, ProdutoDto>().ReverseMap();
            }
        }
    }

  11. Criação dos DTOs

    Crie uma classe DTO para o modelo `Produto`, chamada `ProdutoDto`, que será utilizada na comunicação com o cliente:

    ProdutoDto.cs
    namespace MyApi.Models
    {
        public class ProdutoDto
        {
            public string Nome { get; set; }
            public decimal Preco { get; set; }
        }
    }

  12. Implementação de Testes Unitários com xUnit

    Para criar testes unitários, adicione o pacote xUnit ao seu projeto com o comando abaixo:

    commands
    dotnet add package xunit
    dotnet add package xunit.runner.visualstudio

  13. Exemplo de Testes Unitários para `ProdutoController`

    Crie uma nova classe chamada `ProdutoControllerTests` e escreva testes para validar os métodos do controlador:

    ProdutoControllerTests.cs
    using Microsoft.AspNetCore.Mvc;
    using Moq;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Xunit;
    using MyApi.Controllers;
    using MyApi.Models;
    using MyApi.Repositories;
    
    namespace MyApi.Tests
    {
        public class ProdutoControllerTests
        {
            private readonly Mock<IProdutoRepository> _mockRepo;
            private readonly ProdutoController _controller;
    
            public ProdutoControllerTests()
            {
                _mockRepo = new Mock<IProdutoRepository>();
                _controller = new ProdutoController(_mockRepo.Object);
            }
    
            [Fact]
            public async Task GetAllProdutos_ReturnsOkResult()
            {
                // Arrange
                _mockRepo.Setup(repo => repo.GetAllProdutosAsync()).ReturnsAsync(new List<Produto>());
    
                // Act
                var result = await _controller.GetAllProdutos();
    
                // Assert
                var okResult = Assert.IsType<OkObjectResult>(result.Result);
                Assert.IsType<List<Produto>>(okResult.Value);
            }
        }
    }

  14. Executando a Aplicação e Testes

    Compilar e executar o projeto e testar os endpoints utilizando ferramentas como Postman ou cURL.

    commands
    # Compilar e executar a aplicação
    dotnet run
    # Executar os testes
    dotnet test

    curl_examples
    # Listar todos os produtos
    curl -X GET https://localhost:5001/api/produtos
    # Criar um novo produto
    curl -X POST -H "Content-Type: application/json" -d '{"nome":"Novo Produto", "preco":99.99}' https://localhost:5001/api/produtos

  15. Acessando a Documentação do Swagger

    Após executar a aplicação, você pode acessar a documentação do Swagger em `https://localhost:5001/swagger` para interagir visualmente com sua API.

    commands
    abrir navegador e acessar https://localhost:5001/swagger

Conclusão

Neste tutorial, você aprendeu a desenvolver uma API RESTful robusta utilizando C# e ASP.NET Core, incluindo como configurar o Swagger para documentação automática e o AutoMapper para simplificação do mapeamento de objetos. Exploramos todo o ciclo de desenvolvimento, desde a criação do projeto até a implementação de testes unitários, proporcionando uma base sólida para futuros projetos. Com essas práticas, você poderá desenvolver APIs eficientes e documentadas, facilitando a integração com outras aplicações e serviços. Essa experiência agrega valor às suas habilidades no desenvolvimento de APIs e contribui significativamente para suas futuras empreitadas no mundo do software.

Hashtags

#CSharp #ASPNetCore #APIs #Swagger #AutoMapper #DesenvolvimentoDeSoftware