Crear controladores API en .NET es sencillo cuando entiendes la estructura: inyección de dependencias, rutas claras y buenas prácticas REST con códigos de estado correctos. Aquí verás cómo implementar GET y POST para usuarios y tareas, cómo usar CreatedAtAction y cómo probar todo con Swagger y una base de datos en memoria.
¿Cómo arrancar el controlador API con inyección de dependencias?
Antes de exponer endpoints, se inyecta el servicio en el controlador para aislar la lógica de acceso a datos. Así, el controlador orquesta y el servicio habla con el AppDbContext.
¿Cómo inyectar el servicio en el constructor?
Recibe la interfaz del servicio en el constructor.
Asigna a un campo privado con guion bajo como convención.
Mantén el controlador delgado y sin lógica de datos.
Servicio: intermediario entre controlador y AppDbContext.
Controlador: expone endpoints para ser consumidos por otras aplicaciones.
¿Qué endpoints GET y POST necesitas y cuál es su ruta?
Las rutas siguen la convención: api/nombre-del-controlador. Para usuarios: api/user. La diferencia entre obtener y crear no es la ruta, sino el verbo: GET para leer, POST para crear.
¿Cómo implementar get all y get by id?
GET api/user: devuelve la lista de usuarios con await al método asíncrono del servicio.
GET api/user/{id}: recibe un id y devuelve 404 con NotFound() si no existe.
[HttpGet]publicasyncTask<ActionResult>GetAll(){var users =await _userService.GetAllAsync();returnOk(users);// 200 OK}[HttpGet("{id:int}")]publicasyncTask<ActionResult>GetById(int id){var user =await _userService.GetByIdAsync(id);if(user isnull)returnNotFound();// 404 Not FoundreturnOk(user);// 200 OK}
Buenas prácticas: usa nombres en plural cuando retornas colecciones (users).
Rutas: api/user y api/user/{id} con restricción de tipo int.
¿Cómo devolver created con CreatedAtAction?
Para crear, usa POST y retorna 201 con CreatedAtAction, apuntando al método que lee por id. Así confirmas que el elemento existe y devuelves la ubicación lógica.
[HttpPost]publicasyncTask<ActionResult>Create([FromBody]User user){var created =await _userService.CreateAsync(user);returnCreatedAtAction(nameof(GetById),// acción de lecturanew{ id = created.Id },// ruta con id created // cuerpo completo);// 201 Created}
Razón: CreatedAtAction cumple el patrón REST para creación.
Detalle importante: pasar el nombre del método con nameof evita errores al renombrar.
¿Cómo probar con Swagger y una base de datos en memoria?
Con Swagger, puedes ejecutar GET y POST fácilmente. Al usar una base en memoria, debes proporcionar manualmente el Id en los POST, porque no hay generación automática.
¿Cómo probar con Swagger y datos en memoria?
Crea un usuario con POST a api/user: retorna 201 Created y el objeto creado.
Ejecuta GET api/user: verás la lista con el usuario recién creado.
Crea una tarea con POST a api/task: usa un userId existente, define id de la tarea, título y estado (por ejemplo, completa = false).
Ejecuta GET api/task: devuelve la colección de tareas.
Notas prácticas:
Si la documentación de Swagger muestra un esquema incorrecto para la tarea, envía solo el userId válido y los campos de la tarea.
Asegúrate de registrar controladores: services.AddControllers() y app.MapControllers(). Ambos son necesarios para que los endpoints estén activos.
¿Qué queda pendiente para completar el CRUD?
Implementar actualizar: método PUT o PATCH según el caso.
Implementar eliminar: método DELETE por id.
Mantener consistencia de códigos: 200/204 para actualización exitosa, 404 cuando no exista el recurso.
Habilidades y conceptos aplicados:
Inyección de dependencias para servicios en controladores.
Asincronía con Task y await para IO no bloqueante.
Buenas prácticas REST con Ok, NotFound y CreatedAtAction.
Diseño de rutas: api/user y api/user/{id:int}.
Pruebas con Swagger y base de datos en memoria.
¿Te quedaste con dudas o quieres compartir cómo implementaste update y delete? Deja tu comentario y cuéntanos qué retos encontraste.