Crear Web API práctica y funcional con .Net Core y C#
Crearemos un API para una biblioteca, básico pero totalmente funcional desde cero y paso a paso con C# y .Net Core

Para el desarrollo de este proyecto utilizaremos las siguientes tecnologías y herramientas:

  • Visual Studio 2019
  • C# y .Net Core 5.0
  • SQL Server
  • Entity Framework (con la técnica Code First)

Entorno y dependencia de paquetes


Lo primero será crear un proyecto con Visual Studio 2019 (Puedes usar otra versión si lo deseas pero recomiendo que sea ésta) de tipo Aplicación web ASP.NET Core, la llamaremos APIBiblioteca y será de tipo Core Web API, como se muestra en las siguientes imágenes:

Ahora instalaremos los paquetes necesarios para que funcione el Entity Framework en un proyecto .Net Core y Newtonsoft para funcionalidades con serialización y deserialización de json, éstos son:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.AspNetCore.Mvc.NewtonsoftJson

Estas dependencias se agregan desde el Nugget como se muestra a continuación:

Instalamos de la siguiente manera:

Estructura y configuraciones


Una vez instalados todos los paquetes que necesitamos, es momento de crear la estructura de carpetas del proyecto, así que creamos la carpeta Entities, en donde irán todas las entidades que se corresponderán cada una con una tabla de la base de datos, recordemos que utilizaremos Entity Framework con la técnica Code first.

💡 La técnica Code First consiste en crear una BD por completo a partir de nuestras entidades

En la carpeta Entities creamos dos clases: Autor y Libro como sigue:

    public class Autor
    {
        public int Id { get; set; }
        [Required]
        public string Nombre { get; set; }
        public List< Libro> Libros { get; set; }
    }

    public class Libro
    {
        public int Id { get; set; }
        [Required]
        public string Titulo { get; set; }
        [Required]
        public int AutorId { get; set; }
        public Autor Autor { get; set; }
    }

Recuerda añadir también la siguiente referencia para que puedas usar la anotación Required:

using System.ComponentModel.DataAnnotations;

Creamos la carpeta Context y dentro, la clase ApplicationDBContext:

using APIBiblioteca.Entities;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace APIBiblioteca.Context
{
    public class ApplicationDBContext:DbContext
    {
        public ApplicationDBContext(DbContextOptions< ApplicationDBContext> options) : base(options)
        {

        }

        public DbSet< Autor> Autor { get; set; }
        public DbSet< Libro> Libro { get; set; }
    }
}

Ahora debemos configurar la clase Startup, la abrimos y actualizamos el método ConfigureServices añadiendo las líneas resaltadas:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext< ApplicationDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnectionString")));
            services.AddControllers().AddNewtonsoftJson(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

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

Crearemos ahora la cadena de conexión, nos dirigimos al appsettings.json y añadimos lo siguiente:

{
  "ConnectionStrings": {
    "DefaultConnectionString": "server=.;initial catalog=BibliotecaBD;user=sa;password=12345"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

💡Cambiar el password por el que tiene tu servidor de BD así como los demás datos de la cadena de conexión si fuese necesario

💡Hay una forma más segura de guardar la cadena de conexión, pero por el momento es suficiente, en futuros posts les mostraré opciones mas profesionales y seguras.

Es momento de aplicar Entity Framework, en .Net Core esto se hace por consola, así que nos dirigimos a:

Ejecutamos los siguientes comandos:

add-migration initial

Esto creará la carpeta Migrations y una clase en donde se alojarán todas las migraciones, es decir todas las "versiones" por así decirlo de nuestra BD conforme vayamos actualizando nuestro modelo a través de las entidades:

Ahora ejecutamos el siguiente comando, lo que hará será crear nuestras tablas en la BD incluyendo relaciones, según nuestras entidades

update-database

Checamos en el SQL Management Studio y genial, allí está! 🎉

Cada vez que hagamos un cambio en nuestras entidades y queramos reflejarlas en nuestra base de datos debemos ejecutar los comandos siguientes uno después del otro:

add-migration nombreQueElijas
update-database

Controllers


Vamos a crear nuestros dos controladores: AutorController y LibroController:

💡Si en tu carpeta Controllers ves uno llamado WeatherForecast, puedes eliminarlo, VS lo crea como ejemplo

Veamos como quedaría el controlador AutorController:

using APIBiblioteca.Context;
using APIBiblioteca.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace APIBiblioteca.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AutorController : ControllerBase
    {
        private readonly ApplicationDBContext context;

        public AutorController(ApplicationDBContext context)
        {
            this.context = context;
        }

        [HttpGet]
        public ActionResult< IEnumerable< Autor>> Get()
        {
            return context.Autor.Include(x => x.Libros).ToList();
        }

        [HttpGet("{id}", Name = "ObtenerAutor")]
        public ActionResult< Autor> Get(int id)
        {
            var autor = context.Autor.Include(x => x.Libros).FirstOrDefault(x => x.Id == id);
            if (autor == null)
            {
                return NotFound();
            }
            return autor;
        }

        [HttpPost]
        public ActionResult Post([FromBody] Autor autor)
        {
            context.Autor.Add(autor);
            context.SaveChanges();

            return new CreatedAtRouteResult("ObtenerAutor", new { id = autor.Id }, autor);
        }

        [HttpPut("{id}")]
        public ActionResult Put([FromBody] Autor autor, int id)
        {
            if (id != autor.Id)
            {
                return BadRequest();
            }

            context.Entry(autor).State = EntityState.Modified;
            context.SaveChanges();
            return Ok();
        }

        [HttpDelete("{id}")]
        public ActionResult< Autor> Delete(int id)
        {
            var autor = context.Autor.FirstOrDefault(x => x.Id == id);

            if (autor == null)
            {
                return NotFound();
            }
            //tambien puede usarse context.Autor.Remove(autor);
            context.Entry(autor).State = EntityState.Deleted;
            context.SaveChanges();
            return autor;
        }
    }
}

Y ahora LibroController:

using APIBiblioteca.Context;
using APIBiblioteca.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace APIBiblioteca.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class LibroController : ControllerBase
    {
        private readonly ApplicationDBContext context;

        public LibroController(ApplicationDBContext context)
        {
            this.context = context;
        }


        [HttpGet]
        public ActionResult< List< Libro>> Get()
        {
            var libros = context.Libro.Include(x => x.Autor).ToList();
            return libros;
        }

        [HttpGet("{id}", Name = "ObtenerLibro")]
        public ActionResult< Libro> Get(int id)
        {
            var libro = context.Libro.Include(x => x.Autor).FirstOrDefault(x => x.Id == id);

            if (libro == null)
            {
                return NotFound();
            }
            return libro;
        }

        [HttpPost]
        public ActionResult Post([FromBody] Libro libro)
        {
            context.Libro.Add(libro);
            context.SaveChanges();
            return new CreatedAtRouteResult("ObtenerLibro", new { id = libro.Id }, libro);
        }

        [HttpPut("{id}")]
        public ActionResult Put([FromBody] Libro libro, int id)
        {
            if (id != libro.Id)
            {
                return BadRequest();
            }
            context.Entry(libro).State = EntityState.Modified;
            context.SaveChanges();
            return Ok();
        }

        [HttpDelete("{id}")]
        public ActionResult< Libro> Delete(int id)
        {
            var libro = context.Libro.FirstOrDefault(x => x.Id == id);

            if (libro == null)
            {
                return NotFound();
            }
            context.Libro.Remove(libro);
            context.SaveChanges();
            return libro;
        }

    }
}

Sólo para que la tengas clara, la estructura del proyecto quedaría así:

Pruebas


Para las pruebas usaremos POSTMAN, veamos: Primero ejecutamos el proyecto (F5 o Ctrl + F5) y luego vamos a crear un nuevo autor, es decir probaremos el método POST del controlador Autor:

Todo parece estar bien, revisemos la BD:

💡 Recuerda que para probar algunos métodos de un API como POST y PUT deberás enviarle un body en json, como se muestra resaltado en las imágenes.

Esto es genial, funciona! 😁 ahora probemos el método GET:

No podría ser mejor amigos, todo nos salió a la perfección. ✨ Sigamos con las pruebas, actualicemos el autor, vamos a agregarle el apellido, en otras palabras pondremos a prueba al método PUT:

Ahora vamos a poner a prueba el método DELETE, eliminándome de la lista de autores 😑

Excelente amigos, podemos decir misión cumplida? No aún no, tu tarea será probar el controlador Libros! Vamos anímate, la pelota está en tu cancha estimado dev 😉

El código fuente está en mi cuenta de Github, pero te recomiendo que lo codees tu mismo y si lo descargas sólo sea para comparar.

Si te ha servido este contenido, me ayudarías mucho a mi y sobretodo a la comunidad dev compartiéndolo, un saludo crack!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *