En esta página
Introducción a ASP.NET Core 10 y Minimal APIs
¿Qué es ASP.NET Core?
ASP.NET Core es el framework web de .NET para construir APIs, aplicaciones web, WebSockets y microservicios. Es de código abierto, multiplataforma y uno de los frameworks web más rápidos del mundo según los benchmarks de TechEmpower.
ASP.NET Core 10 trae:
- Minimal APIs maduros y optimizados
- Native AOT mejorado para binarios ultra-pequeños
- OpenAPI 3.1 con generación automática de documentación
- Rate limiting nativo
- WebSockets mejorados
Crear un proyecto web
# Crear una Minimal API
dotnet new webapi -n CatalogoApi -f net10.0 --use-minimal-apis
# Crear una API con controladores
dotnet new webapi -n CatalogoApi -f net10.0
# Ejecutar en modo desarrollo
dotnet run --launch-profile https
# Ejecutar con hot reload
dotnet watch runEl proyecto generado tiene esta estructura:
CatalogoApi/
├── Program.cs ← Punto de entrada + configuración
├── CatalogoApi.csproj ← Archivo de proyecto
├── appsettings.json ← Configuración de la app
├── appsettings.Development.json ← Configuración de desarrollo
└── Properties/
└── launchSettings.json ← Perfiles de ejecución localEl builder pattern de ASP.NET Core
ASP.NET Core usa el builder pattern para configurar la aplicación en dos fases:
Fase 1: builder.Services.* → Registrar servicios (DI container)
↓
builder.Build() → Crear la aplicación
↓
Fase 2: app.Use* → Configurar middleware
app.Map* → Definir endpoints
↓
app.Run() → Iniciar el servidorvar builder = WebApplication.CreateBuilder(args);
// Fase 1: Servicios
builder.Services.AddSingleton<ICache, MemoryCache>();
builder.Services.AddScoped<IProductoRepo, ProductoRepo>();
builder.Services.AddTransient<IEmailService, SmtpEmailService>();
var app = builder.Build();
// Fase 2: Middleware y endpoints
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", () => "¡Bienvenido a la API!");
app.Run();Lifetimes de servicios
| Lifetime | Se crea | Cuando usar |
|---|---|---|
Singleton |
Una vez por aplicación | Caché, configuración, clientes HTTP |
Scoped |
Una vez por solicitud HTTP | DbContext, Unit of Work |
Transient |
Cada vez que se inyecta | Servicios ligeros, stateless |
builder.Services.AddSingleton<IConfiguracion, Configuracion>(); // compartido
builder.Services.AddScoped<IUsuarioService, UsuarioService>(); // por request
builder.Services.AddTransient<IEmailSender, EmailSender>(); // nueva instanciaMinimal API endpoints
Las Minimal APIs definen endpoints con funciones lambda directamente:
// GET simple — devuelve texto plano
app.MapGet("/", () => "API en línea");
// GET con parámetro de ruta y query string
app.MapGet("/productos/{id:int}", (int id, bool? incluirDetalle) =>
{
// id viene de la ruta, incluirDetalle del query string (?incluirDetalle=true)
return Results.Ok(new { id, incluirDetalle });
});
// POST con body JSON
app.MapPost("/productos", (CrearProductoDto dto) =>
{
// ASP.NET deserializa el JSON automáticamente
Console.WriteLine($"Creando: {dto.Nombre}");
return Results.Created("/productos/1", dto);
});
// Agrupar endpoints con RouteGroupBuilder
var grupo = app.MapGroup("/api/v1/productos");
grupo.MapGet("/", ObtenerTodos);
grupo.MapGet("/{id}", ObtenerPorId);
grupo.MapPost("/", Crear);Inyección de dependencias en endpoints
// La DI funciona por parámetros en los endpoints
app.MapGet("/users", (IUserService userService, ILogger<Program> logger) =>
{
logger.LogInformation("Consultando usuarios");
return Results.Ok(userService.ObtenerTodos());
});
// También puedes inyectar desde el contenedor directamente
app.MapGet("/config", (IServiceProvider sp) =>
{
var config = sp.GetRequiredService<IConfiguration>();
return Results.Ok(new { version = config["App:Version"] });
});Configuración (appsettings.json)
{
"App": {
"Nombre": "Catálogo API",
"Version": "1.0.0",
"MaxResultados": 50
},
"ConnectionStrings": {
"Default": "Host=localhost;Database=catalogo;Username=app;Password=pass"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}// Leer configuración fuertemente tipada
public class AppSettings
{
public string Nombre { get; set; } = string.Empty;
public string Version { get; set; } = string.Empty;
public int MaxResultados { get; set; } = 20;
}
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("App"));
// Inyectar en endpoints
app.MapGet("/info", (IOptions<AppSettings> opts) =>
Results.Ok(new { opts.Value.Nombre, opts.Value.Version }));Variables de entorno y entornos de ejecución
// Detectar el entorno actual
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseDeveloperExceptionPage();
}
else if (app.Environment.IsProduction())
{
app.UseExceptionHandler("/error");
app.UseHsts();
}
// Variables de entorno sobreescriben appsettings.json
// ASPNETCORE_ENVIRONMENT=Production
// ConnectionStrings__Default=...Práctica
- API completa: Crea una Minimal API con endpoints CRUD para una entidad
Tarea(int Id, string Titulo, bool Completada)usando un servicio en memoria. - Configuración: Agrega una sección
"Api": { "MaxItems": 10 }enappsettings.jsony úsala en el endpointGET /tareaspara limitar los resultados. - Swagger: Asegúrate de que tu API tenga Swagger habilitado en desarrollo. Usa
.WithName()y.WithSummary()para documentar los endpoints.
En la siguiente lección aprenderemos a organizar APIs más grandes usando controladores, atributos de routing y validación con DataAnnotations.
Minimal APIs para proyectos pequeños y microservicios
Minimal APIs son perfectas para microservicios, APIs de propósito único y proyectos que priorizan la simplicidad. Para APIs grandes con muchos endpoints, considera usar Controllers (ApiController) que ofrecen mejor organización con herencia y filtros.
Results helpers
ASP.NET Core 10 incluye el tipo estático Results con helpers para las respuestas más comunes: Results.Ok(), Results.Created(), Results.NotFound(), Results.BadRequest(), Results.NoContent(), Results.Unauthorized(), Results.Problem(). Usa siempre estos helpers en lugar de devolver datos directamente.
Registra los servicios en el orden correcto
Los servicios se registran ANTES de llamar a builder.Build(). El middleware se configura DESPUÉS del Build(). Si registras un servicio después del Build, obtendrás una excepción en tiempo de ejecución. La separación entre configuración de servicios y middleware es fundamental en ASP.NET Core.
// Program.cs — ASP.NET Core 10 Minimal API completa
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
// ── Registrar servicios (Dependency Injection) ───────
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IProductoService, ProductoService>();
var app = builder.Build();
// ── Configurar el pipeline de middleware ─────────────
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
// ── Definir endpoints ────────────────────────────────
app.MapGet("/productos", (IProductoService svc) =>
Results.Ok(svc.ObtenerTodos()))
.WithName("GetProductos")
.WithOpenApi();
app.MapGet("/productos/{id:int}", (int id, IProductoService svc) =>
{
var producto = svc.ObtenerPorId(id);
return producto is null
? Results.NotFound(new { mensaje = $"Producto {id} no encontrado" })
: Results.Ok(producto);
});
app.MapPost("/productos", (CrearProductoDto dto, IProductoService svc) =>
{
var producto = svc.Crear(dto);
return Results.Created($"/productos/{producto.Id}", producto);
});
app.MapPut("/productos/{id:int}", (int id, ActualizarProductoDto dto, IProductoService svc) =>
{
var actualizado = svc.Actualizar(id, dto);
return actualizado is null ? Results.NotFound() : Results.Ok(actualizado);
});
app.MapDelete("/productos/{id:int}", (int id, IProductoService svc) =>
svc.Eliminar(id) ? Results.NoContent() : Results.NotFound());
app.Run();
Inicia sesión para guardar tu progreso