Skip to content

Guía para crear APIs en C# con ASP.NET Core ​

1. ¿Qué es una API? ​

Una API permite que diferentes partes de una aplicación se comuniquen mediante solicitudes y respuestas HTTP.

En ASP.NET Core, un controlador API recibe solicitudes, ejecuta la lógica correspondiente y devuelve una respuesta al cliente.

Por ejemplo:

  • GET: consultar información.
  • POST: crear un recurso o ejecutar una operación.
  • PUT: reemplazar o actualizar un recurso.
  • PATCH: modificar parcialmente un recurso.
  • DELETE: eliminar un recurso.

2. Estructura básica de un controlador API ​

csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;

namespace MiProyecto.Controllers.Api
{
    [Route("api/productos")]
    [ApiController]
    public class ProductosApiController : ControllerBase
    {
        [HttpGet]
        public ActionResult ObtenerProductos()
        {
            return Ok("Lista de productos");
        }
    }
}

Anotaciones principales del controlador ​

AnotaciónFunción
[ApiController]Activa comportamientos propios de las APIs, como respuestas automáticas ante errores de validación del modelo.
[Route("api/productos")]Define la ruta base del controlador.
[Authorize]Exige que la solicitud cumpla los requisitos de autenticación configurados.
[AllowAnonymous]Permite acceder a una acción sin autenticación, incluso si existe una política de autorización aplicable al controlador.

Importante: para que los controladores con rutas por atributos funcionen, normalmente se registra app.MapControllers() en Program.cs.

3. Anotaciones para los métodos ​

Las anotaciones HTTP definen qué tipo de solicitud puede ejecutar una acción.

[HttpGet] ​

Se utiliza para consultar información.

csharp
[HttpGet]
public ActionResult ObtenerProductos()
{
    return Ok();
}

También puedes definir una ruta específica:

csharp
[HttpGet("{id}")]
public ActionResult ObtenerProducto(int id)
{
    return Ok();
}

La segunda acción corresponde a una ruta como GET /api/productos/5.

[HttpPost] ​

Se utiliza para crear recursos o ejecutar operaciones que reciben datos.

csharp
[HttpPost]
public ActionResult CrearProducto()
{
    return Ok();
}

Puedes agregar una ruta:

csharp
[HttpPost("calcular")]
public ActionResult Calcular()
{
    return Ok();
}

Si la ruta base es api/pedidos, la ruta final será POST /api/pedidos/calcular.

[HttpPut] ​

Se utiliza normalmente para reemplazar o actualizar un recurso existente.

csharp
[HttpPut("{id}")]
public ActionResult ActualizarProducto(int id)
{
    return Ok();
}

[HttpPatch] ​

Se utiliza para modificar parcialmente un recurso.

csharp
[HttpPatch("{id}")]
public ActionResult ActualizarParcialmente(int id)
{
    return Ok();
}

[HttpDelete] ​

Se utiliza para eliminar un recurso.

csharp
[HttpDelete("{id}")]
public ActionResult EliminarProducto(int id)
{
    return NoContent();
}

4. ¿Cómo recibe parámetros una acción? ​

Los datos pueden llegar mediante la URL, la cadena de consulta o el cuerpo de la solicitud.

Parámetros de ruta: [FromRoute] ​

Se reciben desde una parte de la URL.

csharp
[HttpGet("{id}")]
public ActionResult ObtenerProducto([FromRoute] int id)
{
    return Ok(id);
}

Ejemplo de solicitud:

GET /api/productos/5

El valor de id será 5.

En muchos casos, ASP.NET Core puede inferir el origen del parámetro, por lo que [FromRoute] puede ser opcional. Es útil cuando quieres dejar explícito de dónde proviene.

Parámetros de consulta: [FromQuery] ​

Se reciben desde la cadena de consulta de la URL.

csharp
[HttpGet("buscar")]
public ActionResult BuscarProductos([FromQuery] string q)
{
    return Ok(q);
}

Ejemplo:

GET /api/productos/buscar?q=llanta

El parámetro q recibe el texto llanta.

Es habitual para filtros, búsquedas, paginación y ordenamiento.

Cuerpo de la solicitud: [FromBody] ​

Se utiliza para recibir datos enviados en el cuerpo HTTP, generalmente en formato JSON.

csharp
[HttpPost]
public ActionResult CrearProducto([FromBody] CrearProductoDto producto)
{
    return Ok(producto);
}

Ejemplo de JSON:

json
{
  "nombre": "Llanta deportiva",
  "precio": 45000
}

ASP.NET Core convierte el JSON en un objeto del tipo esperado, siempre que la estructura y los tipos sean compatibles.

Nota: normalmente se usa un solo parámetro ligado al cuerpo ([FromBody]) por acción. Para recibir varios datos relacionados, es habitual agruparlos en un DTO.

Otros orígenes de datos ​

AnotaciónUso
[FromForm]Recibir datos de formularios, incluidos archivos enviados mediante multipart/form-data.
[FromHeader]Obtener un valor de una cabecera HTTP.
[FromServices]Obtener un servicio registrado en el contenedor de dependencias.

5. Validaciones de los datos recibidos ​

Los DTO pueden utilizar anotaciones de validación de System.ComponentModel.DataAnnotations.

csharp
using System.ComponentModel.DataAnnotations;

public class CrearProductoDto
{
    [Required]
    [StringLength(100)]
    public string Nombre { get; set; } = string.Empty;

    [Range(0.01, double.MaxValue)]
    public decimal Precio { get; set; }
}

Anotaciones importantes ​

AnotaciónFunción
[Required]Exige un valor.
[StringLength(100)]Limita la longitud de una cadena.
[MinLength(3)]Establece una longitud mínima.
[MaxLength(100)]Establece una longitud máxima.
[Range(1, 100)]Exige que el valor esté dentro de un intervalo.
[EmailAddress]Valida el formato de una dirección de correo.
[RegularExpression("...")]Valida un valor mediante una expresión regular.
[Compare("OtraPropiedad")]Compara el valor con otra propiedad.

Con [ApiController], los errores de validación del modelo normalmente generan automáticamente una respuesta 400 Bad Request, sin que tengas que comprobar manualmente ModelState en cada acción.

Importante: las anotaciones no sustituyen las validaciones de negocio. Por ejemplo, [Range] puede validar que una cantidad sea positiva, pero no comprueba por sí sola que el producto exista o tenga inventario suficiente.

6. ¿Cómo devolver respuestas HTTP? ​

En los controladores que heredan de ControllerBase, puedes utilizar métodos auxiliares para devolver diferentes códigos HTTP.

MétodoCódigoCuándo utilizarlo
Ok(datos)200La solicitud se procesó correctamente y se devuelve información.
CreatedAtAction(...)201Se creó un recurso y se puede indicar cómo consultarlo.
NoContent()204La operación tuvo éxito y no necesita devolver un cuerpo.
BadRequest(mensaje)400Los datos o la solicitud son inválidos.
Unauthorized()401La solicitud no está autenticada correctamente.
Forbid()403El usuario está autenticado, pero no tiene permiso para la operación.
NotFound()404No se encontró el recurso solicitado.
Conflict(mensaje)409Existe un conflicto con el estado actual del recurso.
StatusCode(500)500Error interno del servidor; úsalo con cuidado y sin exponer detalles internos.

Devolver un objeto JSON ​

csharp
return Ok(new
{
    Subtotal = subtotal,
    Impuestos = impuestos,
    Total = subtotal + impuestos
});

ASP.NET Core serializa el objeto a JSON para la respuesta HTTP.

Devolver un error ​

csharp
return BadRequest("La cantidad debe ser mayor que cero");

Esto devuelve un 400 Bad Request con el mensaje.

Devolver un recurso no encontrado ​

csharp
return NotFound($"Producto con ID {id} no encontrado");

Devolver una respuesta sin contenido ​

csharp
return NoContent();

Se utiliza, por ejemplo, cuando una eliminación se completó correctamente y no es necesario devolver datos.

Devolver un recurso recién creado ​

csharp
return CreatedAtAction(
    nameof(ObtenerProducto),
    new { id = producto.Id },
    producto
);

Esto normalmente devuelve 201 Created, incluye el recurso en el cuerpo y genera una referencia a la acción que permite consultarlo.

Este ejemplo supone que existe una acción ObtenerProducto compatible con el parámetro id.

7. ¿Qué tipo de retorno debería utilizar? ​

IActionResult ​

Permite devolver diferentes tipos de respuesta HTTP sin declarar un tipo concreto de cuerpo.

csharp
public IActionResult EliminarProducto(int id)
{
    if (id <= 0)
        return BadRequest();

    return NoContent();
}

ActionResult ​

También permite devolver diferentes respuestas HTTP, pero no declara un tipo específico para el cuerpo de respuesta.

csharp
public async Task<ActionResult> Calcular()
{
    // Lógica de cálculo

    return Ok();
}

ActionResult<T> ​

Es útil cuando quieres indicar el tipo de dato que devuelve una acción, conservando la posibilidad de devolver errores HTTP.

csharp
public ActionResult<ProductoDto> ObtenerProducto(int id)
{
    // Buscar el producto

    return Ok(new ProductoDto());
}

Regla práctica:

  • Usa ActionResult<T> cuando la acción devuelve un tipo de dato definido.
  • Usa IActionResult o ActionResult cuando el resultado puede variar entre varias respuestas HTTP y no necesitas declarar un tipo concreto para el cuerpo.

En métodos asíncronos, normalmente se utilizan Task<IActionResult>, Task<ActionResult> o Task<ActionResult<T>>, según corresponda.

8. Ejemplo integrado ​

Este ejemplo reúne una ruta, un parámetro de ruta, una consulta a Entity Framework Core y diferentes respuestas HTTP.

csharp
[HttpGet("{id}")]
public async Task<ActionResult<Producto>> ObtenerProducto(int id)
{
    if (id <= 0)
        return BadRequest("El ID debe ser mayor que cero");

    var producto = await _context.Productos.FindAsync(id);

    if (producto == null)
        return NotFound($"Producto con ID {id} no encontrado");

    return Ok(producto);
}

El método:

  1. Recibe el ID desde la ruta.
  2. Valida que el ID tenga un valor permitido.
  3. Busca el producto por su clave primaria.
  4. Devuelve 404 si no existe.
  5. Devuelve 200 con el producto si existe.

En una API pública, normalmente conviene devolver un DTO en lugar de exponer directamente la entidad de Entity Framework.

9. Errores comunes ​

  • 404 en una ruta: revisa [Route], la anotación HTTP, la URL y el puerto. También verifica que app.MapControllers() esté configurado.
  • 400 al enviar JSON: revisa que el cuerpo tenga la estructura esperada, los tipos sean compatibles y las validaciones se cumplan.
  • 401 o 403: revisa la autenticación, las credenciales y las políticas de autorización.
  • No se ejecuta la acción: verifica el método HTTP utilizado. Un endpoint [HttpPost] no responde a una petición GET.
  • Error al devolver una entidad: revisa las relaciones de navegación y la estructura de los datos serializados; para contratos controlados, considera utilizar DTOs.
  • El cliente envía un precio o total alterado: calcula los valores importantes en el servidor usando datos confiables de la base de datos.

10. Lista rápida antes de implementar un endpoint ​

  • [ ] Definir qué operación realizará.
  • [ ] Elegir el método HTTP adecuado.
  • [ ] Definir la ruta.
  • [ ] Identificar los datos que llegan por ruta, consulta, cabeceras o cuerpo.
  • [ ] Crear un DTO si necesitas controlar los datos recibidos.
  • [ ] Validar los datos y las reglas de negocio.
  • [ ] Ejecutar la lógica y consultar la base de datos cuando corresponda.
  • [ ] Devolver el código HTTP adecuado y una respuesta clara.
  • [ ] Probar los casos válidos y los errores con Postman.

Regla para recordar ​

La ruta identifica el endpoint, las anotaciones definen cómo se invoca y de dónde provienen los datos, las validaciones protegen la operación y el resultado HTTP comunica qué ocurrió.