Desde la configuración inicial del proyecto utilizando la CLI de .NET hasta la configuración de middleware, controladores y servicios, aprenda cada paso para crear una API sólida. Descubra las mejores prácticas para la inyección de dependencias, acciones asincrónicas y manejo de excepciones para crear aplicaciones web escalables y eficientes.
Utilice la CLI de .NET para crear un nuevo proyecto de API web. Esto configura una estructura básica del proyecto que incluye Program.cs para el inicio y un controlador WeatherForecast como ejemplo.
dotnet new webapi -n MyWebApi
.NET 8 continúa la tendencia hacia API mínimas, lo que le permite configurar servicios y puntos finales de manera simplificada y concisa directamente en el archivo Program.cs.
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => "Hello, World!"); app.Run();
Los controladores manejan las solicitudes HTTP entrantes y responden al cliente. Se definen heredando de ControllerBase y anotando con [ApiController].
[ApiController] [Route("[controller]")] public class MyController : ControllerBase { [HttpGet] public IActionResult Get() => Ok("Hello from MyController"); }
La inyección de dependencias (DI) integrada de .NET Core facilita la administración de dependencias. Puede inyectar servicios en sus controladores a través de sus constructores.
public class MyService { public string GetMessage() => "Injected message"; } public class MyController : ControllerBase { private readonly MyService _myService; public MyController(MyService myService) { _myService = myService; } [HttpGet] public IActionResult Get() => Ok(_myService.GetMessage()); }
Los servicios (como contextos de bases de datos, servicios personalizados, etc.) se configuran en el archivo Program.cs, lo que los hace disponibles para la inyección de dependencias en toda su aplicación.
builder.Services.AddScoped<MyService>();
.NET admite archivos de configuración específicos del entorno (appsettings.json, appsettings.Development.json, etc.), lo que permite diferentes configuraciones según el entorno de la aplicación.
// appsettings.Development.json { "Logging": { "LogLevel": { "Default": "Debug" } } }
Los componentes de middleware forman una canalización que maneja solicitudes y respuestas. Se puede crear middleware personalizado para cuestiones transversales como el registro o el manejo de errores.
app.Use(async (context, next) => { // Custom logic before passing to the next middleware await next(); // Custom logic after executing the next middleware });
El enrutamiento en .NET Web API se logra mediante el enrutamiento de atributos en controladores y métodos de acción. Esto permite que las URL se asignen directamente a las acciones del controlador.
[HttpGet("myaction/{id}")] public IActionResult GetAction(int id) => Ok($"Action with ID = {id}");
El enlace de modelo asigna automáticamente datos de solicitudes HTTP a parámetros de métodos de acción. Admite tipos complejos, incluidos cuerpos JSON y parámetros de cadenas de consulta.
public class MyModel { public int Id { get; set; } public string Name { get; set; } } [HttpPost] public IActionResult PostAction([FromBody] MyModel model) => Ok(model);
Las anotaciones de datos se pueden utilizar para validar los datos del modelo. El atributo [ApiController] aplica automáticamente la validación y responde con 400 si el modelo no es válido.
public class MyModel { [Required] public int Id { get; set; } [StringLength(100)] public string Name { get; set; } }
Las acciones asincrónicas mejoran la escalabilidad al liberar subprocesos mientras se espera que se completen las operaciones de E/S. Utilice la palabra clave async y devuelva Task o Task<IActionResult>.
[HttpGet("{id}")] public async Task<IActionResult> GetAsync(int id) { var result = await _service.GetByIdAsync(id); return Ok(result); }
El manejo global de excepciones permite el procesamiento centralizado de errores, el registro y las respuestas API estandarizadas en excepciones no controladas.
app.UseExceptionHandler(a => a.Run(async context => { var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); var exception = exceptionHandlerPathFeature.Error; // Log the exception, generate a custom response, etc. context.Response.StatusCode = 500; await context.Response.WriteAsJsonAsync(new { Error = "An unexpected error occurred" }); }));
El control de versiones de API ayuda a gestionar los cambios en la API a lo largo del tiempo. La plataforma .NET admite el control de versiones mediante una cadena de consulta, una ruta URL o un encabezado de solicitud.
builder.Services.AddApiVersioning(options => { options.AssumeDefaultVersionWhenUnspecified = true; options.DefaultApiVersion = new ApiVersion(1, 0); options.ReportApiVersions = true; });
La negociación de contenido permite que una API proporcione diferentes formatos de respuesta según el encabezado Aceptar de la solicitud, lo que permite la compatibilidad con formatos como JSON, XML, etc.
builder.Services.AddControllers() .AddXmlDataContractSerializerFormatters();
Personalice el formato de respuesta JSON, como nombrar camelCase o ignorar valores nulos, configurando los ajustes del serializador JSON.
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.IgnoreNullValues = true; });
El intercambio de recursos entre orígenes (CORS) permite llamar a su API desde aplicaciones web alojadas en diferentes dominios. Configure la política CORS según sus requisitos.
builder.Services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", builder => builder.WithOrigins("http://example.com")); }); app.UseCors("AllowSpecificOrigin");
Proteja su API habilitando la autenticación, que verifica la identidad de los usuarios o servicios que realizan solicitudes.
builder.Services.AddAuthentication("Bearer") .AddJwtBearer(options => { options.Authority = "https://your-auth-server"; options.Audience = "your-api"; });
Después de la autenticación, la autorización determina si un usuario autenticado tiene permiso para realizar una acción o acceder a un recurso.
[Authorize] public class SecureController : ControllerBase { // Action methods here }
Swagger (OpenAPI) proporciona documentación interactiva para su API, lo que permite a los desarrolladores comprenderla y consumirla fácilmente.
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); app.UseSwagger(); app.UseSwaggerUI();
.NET Core proporciona un marco de registro integrado que puede registrar mensajes en varias salidas (consola, ventana de depuración, servicios externos, etc.).
logger.LogInformation("This is an informational message"); app.Use(async (context, next) => { logger.LogError("This is an error message before the next middleware"); await next.Invoke(); // Log after calling the next middleware });
Entity Framework Core es un ORM utilizado para el acceso a datos en aplicaciones .NET. Le permite consultar y manipular datos utilizando objetos fuertemente tipados.
public class MyDbContext : DbContext { public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) {} public DbSet<MyModel> MyModels { get; set; } }
Las migraciones le permiten aplicar control de versiones al esquema de su base de datos mediante el seguimiento de los cambios en sus modelos de datos.
dotnet ef migrations add InitialCreate dotnet ef database update
El patrón Repositorio abstrae la capa de datos, lo que hace que su aplicación sea más modular y más fácil de mantener.
public interface IRepository<T> { Task<IEnumerable<T>> GetAllAsync(); Task<T> GetByIdAsync(int id); // Other methods... } public class MyRepository<T> : IRepository<T> where T : class { private readonly MyDbContext _context; public MyRepository(MyDbContext context) { _context = context; } // Implement methods... }
Las pruebas unitarias garantizan que su API web funcione correctamente al probar unidades individuales de código de forma aislada.
public class MyControllerTests { [Fact] public async Task Get_ReturnsExpectedValue() { // Arrange var serviceMock = new Mock<IMyService>(); serviceMock.Setup(service => service.GetAsync()).ReturnsAsync("test"); var controller = new MyController(serviceMock.Object); // Act var result = await controller.Get(); // Assert Assert.Equal("test", result.Value); } }
.NET Web API puede servir como backend para una aplicación front-end, proporcionando servicios RESTful.
fetch('https://localhost:5001/mycontroller') .then(response => response.json()) .then(data => console.log(data));
Las comprobaciones de estado proporcionan una forma de monitorear el estado de su aplicación y sus dependencias, lo que es útil para arquitecturas de microservicios.
builder.Services.AddHealthChecks(); app.MapHealthChecks("/health");
SignalR habilita la funcionalidad web en tiempo real, lo que permite que el código del lado del servidor envíe notificaciones asincrónicas a las aplicaciones web del lado del cliente.
public class MyHub : Hub { public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } }
El almacenamiento en caché de respuestas reduce la cantidad de solicitudes que un servidor debe manejar al almacenar una copia de los recursos solicitados previamente.
[HttpGet("{id}")] [ResponseCache(Duration = 60)] public IActionResult GetById(int id) { // Retrieve and return your resource }
Servir archivos estáticos (HTML, CSS, JavaScript, etc.) es esencial para respaldar aplicaciones front-end con una API web .NET.
app.UseStaticFiles(); // Enable static file serving
El patrón de opciones utiliza clases para representar grupos de configuraciones relacionadas. Usando IOptions<T>, puede acceder a estas configuraciones en cualquier lugar de su aplicación.
public class MySettings { public string Setting1 { get; set; } // Other settings } builder.Services.Configure<MySettings>(builder.Configuration.GetSection("MySettings")); public class MyService { private readonly MySettings _settings; public MyService(IOptions<MySettings> settings) { _settings = settings.Value; } // Use _settings.Setting1 }
El middleware es un software que se ensambla en una canalización de aplicaciones para manejar solicitudes y respuestas. Se puede crear middleware personalizado para realizar tareas específicas.
public class MyCustomMiddleware { private readonly RequestDelegate _next; public MyCustomMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext) { // Pre-processing logic here await _next(httpContext); // Call the next middleware in the pipeline // Post-processing logic here } } // Extension method for easy middleware registration public static class MyCustomMiddlewareExtensions { public static IApplicationBuilder UseMyCustomMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<MyCustomMiddleware>(); } }
La limitación de velocidad protege su API del uso excesivo al limitar la frecuencia con la que un usuario puede realizar solicitudes dentro de un período de tiempo determinado.
// Assume using a third-party library like AspNetCoreRateLimit builder.Services.AddInMemoryRateLimiting(); builder.Services.Configure<IpRateLimitOptions>(options => { options.GeneralRules = new List<RateLimitRule> { new RateLimitRule { Endpoint = "*", Limit = 100, Period = "1h" } }; });
Las claves API son una forma sencilla de autenticar y autorizar llamadas API. Se pasan del cliente al servidor en la cadena de consulta o en el encabezado.
public class ApiKeyMiddleware { private readonly RequestDelegate _next; private const string APIKEYNAME = "x-api-key"; public ApiKeyMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { if (!context.Request.Headers.TryGetValue(APIKEYNAME, out var extractedApiKey)) { context.Response.StatusCode = 401; await context.Response.WriteAsync("API Key was not provided."); return; } // Validate the extracted API Key here... await _next(context); } }
El almacenamiento en caché de resultados le permite almacenar la respuesta a una solicitud. Las solicitudes posteriores se pueden atender desde la memoria caché, lo que mejora significativamente el rendimiento.
[HttpGet] [ResponseCache(Duration = 120, Location = ResponseCacheLocation.Client, NoStore = false)] public IActionResult Get() { // Your logic here }
Las tareas en segundo plano permiten que las operaciones se ejecuten en segundo plano, independientemente de las solicitudes de los usuarios, como enviar correos electrónicos o procesar trabajos de larga duración.
public class MyBackgroundService : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { // Your background task logic here await Task.Delay(TimeSpan.FromHours(1), stoppingToken); } } }
WebSockets proporciona un canal de comunicación full-duplex a través de una única conexión de larga duración, ideal para aplicaciones en tiempo real.
app.UseWebSockets(); app.Use(async (context, next) => { if (context.WebSockets.IsWebSocketRequest) { WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); // Handle the WebSocket request here } else { await next(); } });
La localización de solicitudes proporciona una forma de localizar contenido para diferentes culturas e idiomas, según la información de la solicitud.
var supportedCultures = new[] { "en-US", "fr-FR" }; var localizationOptions = new RequestLocalizationOptions() .SetDefaultCulture(supportedCultures[0]) .AddSupportedCultures(supportedCultures) .AddSupportedUICultures(supportedCultures); app.UseRequestLocalization(localizationOptions);
GraphQL es un lenguaje de consulta para API. La integración de una API web .NET con GraphQL permite una recuperación de datos más eficiente.
// Assume using a library like HotChocolate builder.Services .AddGraphQLServer() .AddQueryType<Query>(); app.MapGraphQL();
La supervisión y la telemetría implican recopilar, analizar y actuar sobre datos sobre el rendimiento y el uso de su aplicación.
// Assume using Application Insights builder.Services.AddApplicationInsightsTelemetry("YOUR_INSTRUMENTATION_KEY");
SignalR es una biblioteca que simplifica la adición de funcionalidad web en tiempo real a las aplicaciones. La funcionalidad web en tiempo real es la capacidad de hacer que el código del servidor envíe contenido a los clientes conectados instantáneamente, sin necesidad de que el servidor espere a que un cliente solicite nuevos datos. SignalR es perfecto para desarrollar aplicaciones de chat, paneles de control en tiempo real y aplicaciones web más interactivas.
public class ChatHub : Hub { public async Task SendMessage(string user, string message) { // Call the broadcastMessage method to update clients. await Clients.All.SendAsync("broadcastMessage", user, message); } } // Startup or Program.cs app.MapHub<ChatHub>("/chathub");
Integración en Program.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // Other configurations... app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/chathub"); }); }
Entity Framework Core permite el mapeo de relaciones complejas entre entidades, como uno a uno, uno a muchos y muchos a muchos.
public class Author { public int AuthorId { get; set; } public string Name { get; set; } public ICollection<Book> Books { get; set; } } public class Book { public int BookId { get; set; } public string Title { get; set; } public int AuthorId { get; set; } public Author Author { get; set; } }
Los atributos de validación personalizados le permiten definir su lógica de validación para modelos de datos, ampliando los atributos de validación integrados.
public class MyCustomValidationAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // Your custom validation logic here if (value is int intValue && intValue > 0) { return ValidationResult.Success; } return new ValidationResult("Value must be positive"); } } public class MyModel { [MyCustomValidationAttribute] public int MyProperty { get; set; } }
El patrón de opciones de .NET admite escenarios de configuración complejos, incluidos objetos anidados, listas y validación.
public class MyOptions { public MyNestedOptions Nested { get; set; } public List<string> Items { get; set; } } public class MyNestedOptions { public string Key { get; set; } } // In Program.cs or Startup.cs builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("MyOptions"));
Monitorear y crear perfiles de una aplicación puede identificar cuellos de botella e ineficiencias, esenciales para optimizar el rendimiento.
app.UseMiniProfiler();
Mejore la documentación de su API integrando comentarios XML en su interfaz de usuario Swagger, brindando una experiencia más rica para los desarrolladores que consumen su API.
builder.Services.AddSwaggerGen(c => { var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); });
La globalización y la localización permiten que su aplicación admita múltiples idiomas y culturas, haciéndola accesible a una audiencia global.
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); app.UseRequestLocalization(app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);
Mejorar la seguridad de su aplicación web agregando varios encabezados HTTP puede protegerla contra ataques y vulnerabilidades comunes.
app.UseHsts(); app.UseXContentTypeOptions(); app.UseReferrerPolicy(opts => opts.NoReferrer()); app.UseXXssProtection(options => options.EnabledWithBlockMode()); app.UseXfo(options => options.Deny());
Los indicadores de funciones le permiten activar y desactivar funciones de su aplicación sin implementar código nuevo, lo que facilita las pruebas y las implementaciones.
// Using a library like Microsoft.FeatureManagement builder.Services.AddFeatureManagement();
Blazor le permite crear interfaces de usuario web interactivas utilizando C# en lugar de JavaScript. La integración de Blazor con Web API proporciona una experiencia de desarrollo completa y perfecta.
// In a Blazor Server app @code { private IEnumerable<WeatherForecast> forecasts; protected override async Task OnInitializedAsync() { forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast"); } }
La compresión de respuestas puede reducir el tamaño de las respuestas de su API, mejorando los tiempos de carga para los clientes en redes lentas.
builder.Services.AddResponseCompression(options => { options.Providers.Add<GzipCompressionProvider>(); options.EnableForHttps = true; }); app.UseResponseCompression();
¡Gracias por ser parte de la comunidad C#! Antes de que te vayas:
Síguenos: X | LinkedIn | Dev.to | Hashnodo | Boletín | tumblr
Visita nuestras otras plataformas: GitHub | Instagram | Tiktok | Quora | diario.dev
También publicado aquí