Desde a configuração inicial do projeto usando a CLI do .NET até a configuração de middleware, controladores e serviços, aprenda cada etapa para criar uma API robusta. Descubra as práticas recomendadas para injeção de dependência, ações assíncronas e tratamento de exceções para criar aplicativos Web escalonáveis e eficientes.
Use a CLI do .NET para criar um novo projeto de API Web. Isso configura uma estrutura básica de projeto, incluindo Program.cs para inicialização e um controlador WeatherForecast como exemplo.
dotnet new webapi -n MyWebApi
O .NET 8 continua a tendência de APIs mínimas, permitindo configurar serviços e endpoints de maneira simplificada e concisa diretamente no arquivo Program.cs.
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => "Hello, World!"); app.Run();
Os controladores lidam com solicitações HTTP recebidas e respondem ao cliente. Eles são definidos herdando de ControllerBase e anotando com [ApiController].
[ApiController] [Route("[controller]")] public class MyController : ControllerBase { [HttpGet] public IActionResult Get() => Ok("Hello from MyController"); }
A injeção de dependência (DI) integrada do .NET Core facilita o gerenciamento de dependências. Você pode injetar serviços em seus controladores por meio de seus construtores.
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()); }
Os serviços (como contextos de banco de dados, serviços personalizados, etc.) são configurados no arquivo Program.cs, tornando-os disponíveis para injeção de dependência em todo o seu aplicativo.
builder.Services.AddScoped<MyService>();
O .NET oferece suporte a arquivos de configuração específicos do ambiente (appsettings.json, appsettings.Development.json, etc.), permitindo diferentes configurações com base no ambiente do aplicativo.
// appsettings.Development.json { "Logging": { "LogLevel": { "Default": "Debug" } } }
Os componentes de middleware formam um pipeline que trata de solicitações e respostas. Middleware personalizado pode ser criado para questões transversais, como registro ou tratamento de erros.
app.Use(async (context, next) => { // Custom logic before passing to the next middleware await next(); // Custom logic after executing the next middleware });
O roteamento na API Web .NET é obtido por meio do roteamento de atributos em controladores e métodos de ação. Isso permite que URLs sejam mapeados diretamente para ações do controlador.
[HttpGet("myaction/{id}")] public IActionResult GetAction(int id) => Ok($"Action with ID = {id}");
A vinculação de modelo mapeia automaticamente dados de solicitações HTTP para parâmetros de método de ação. Ele oferece suporte a tipos complexos, incluindo corpos JSON e parâmetros de string de consulta.
public class MyModel { public int Id { get; set; } public string Name { get; set; } } [HttpPost] public IActionResult PostAction([FromBody] MyModel model) => Ok(model);
Anotações de dados podem ser usadas para validar dados do modelo. O atributo [ApiController] impõe automaticamente a validação, respondendo com 400 se o modelo for inválido.
public class MyModel { [Required] public int Id { get; set; } [StringLength(100)] public string Name { get; set; } }
As ações assíncronas melhoram a escalabilidade, liberando threads enquanto aguardam a conclusão das operações de E/S. Use a palavra-chave async e retorne Task ou Task<IActionResult>.
[HttpGet("{id}")] public async Task<IActionResult> GetAsync(int id) { var result = await _service.GetByIdAsync(id); return Ok(result); }
O tratamento global de exceções permite processamento centralizado de erros, registro e respostas de API padronizadas em exceções não tratadas.
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" }); }));
O controle de versão da API ajuda a gerenciar alterações na API ao longo do tempo. A plataforma .NET oferece suporte ao controle de versão por meio de string de consulta, caminho de URL ou cabeçalho de solicitação.
builder.Services.AddApiVersioning(options => { options.AssumeDefaultVersionWhenUnspecified = true; options.DefaultApiVersion = new ApiVersion(1, 0); options.ReportApiVersions = true; });
A negociação de conteúdo permite que uma API atenda diferentes formatos de resposta com base no cabeçalho Accept da solicitação, permitindo suporte para formatos como JSON, XML, etc.
builder.Services.AddControllers() .AddXmlDataContractSerializerFormatters();
Personalize a formatação da resposta JSON, como nomenclatura camelCase ou ignorar valores nulos, definindo as configurações do serializador JSON.
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.IgnoreNullValues = true; });
O Cross-Origin Resource Sharing (CORS) permite que sua API seja chamada de aplicativos da web hospedados em domínios diferentes. Configure a política CORS de acordo com seus requisitos.
builder.Services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", builder => builder.WithOrigins("http://example.com")); }); app.UseCors("AllowSpecificOrigin");
Proteja sua API habilitando a autenticação, que verifica a identidade dos usuários ou serviços que fazem solicitações.
builder.Services.AddAuthentication("Bearer") .AddJwtBearer(options => { options.Authority = "https://your-auth-server"; options.Audience = "your-api"; });
Após a autenticação, a autorização determina se um usuário autenticado tem permissão para executar uma ação ou acessar um recurso.
[Authorize] public class SecureController : ControllerBase { // Action methods here }
Swagger (OpenAPI) fornece documentação interativa para sua API, permitindo que os desenvolvedores a entendam e consumam facilmente.
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); app.UseSwagger(); app.UseSwaggerUI();
O .NET Core fornece uma estrutura de log integrada que pode registrar mensagens em várias saídas (console, janela de depuração, serviços 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 é um ORM usado para acesso a dados em aplicativos .NET. Ele permite consultar e manipular dados usando objetos fortemente tipados.
public class MyDbContext : DbContext { public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) {} public DbSet<MyModel> MyModels { get; set; } }
As migrações permitem que você aplique controle de versão ao esquema do seu banco de dados rastreando alterações nos seus modelos de dados.
dotnet ef migrations add InitialCreate dotnet ef database update
O padrão Repository abstrai a camada de dados, tornando seu aplicativo mais modular e mais fácil de manter.
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... }
O teste de unidade garante que sua API Web funcione corretamente, testando unidades individuais de código isoladamente.
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); } }
A API da Web .NET pode servir como back-end para um aplicativo front-end, fornecendo serviços RESTful.
fetch('https://localhost:5001/mycontroller') .then(response => response.json()) .then(data => console.log(data));
As verificações de integridade fornecem uma maneira de monitorar o status do seu aplicativo e suas dependências, o que é útil para arquiteturas de microsserviços.
builder.Services.AddHealthChecks(); app.MapHealthChecks("/health");
O SignalR permite funcionalidade web em tempo real, permitindo que o código do servidor envie notificações assíncronas para aplicativos web do lado do cliente.
public class MyHub : Hub { public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } }
O cache de resposta reduz o número de solicitações que um servidor deve atender, armazenando uma cópia dos recursos solicitados anteriormente.
[HttpGet("{id}")] [ResponseCache(Duration = 60)] public IActionResult GetById(int id) { // Retrieve and return your resource }
Servir arquivos estáticos (HTML, CSS, JavaScript, etc.) é essencial para apoiar aplicativos front-end com uma API Web .NET.
app.UseStaticFiles(); // Enable static file serving
O padrão de opções usa classes para representar grupos de configurações relacionadas. Usando IOptions<T>, você pode acessar essas configurações em qualquer lugar do seu aplicativo.
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 }
Middleware é um software montado em um pipeline de aplicativo para lidar com solicitações e respostas. Middleware personalizado pode ser criado para executar tarefas 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>(); } }
A limitação de taxa protege sua API contra uso excessivo, limitando a frequência com que um usuário pode fazer solicitações dentro de um determinado período.
// 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" } }; });
As chaves de API são uma maneira simples de autenticar e autorizar chamadas de API. Eles são passados do cliente para o servidor na string de consulta ou no cabeçalho.
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); } }
O cache de saída permite armazenar a resposta a uma solicitação. As solicitações subsequentes podem ser atendidas a partir do cache, melhorando significativamente o desempenho.
[HttpGet] [ResponseCache(Duration = 120, Location = ResponseCacheLocation.Client, NoStore = false)] public IActionResult Get() { // Your logic here }
As tarefas em segundo plano permitem que as operações sejam executadas em segundo plano, independentemente das solicitações do usuário, como o envio de e-mails ou o processamento de trabalhos de longa duração.
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 fornecem um canal de comunicação full-duplex em uma conexão única e de longa duração, ideal para aplicações em tempo 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(); } });
A localização de solicitações fornece uma maneira de localizar conteúdo para diferentes culturas e idiomas, com base nas informações da solicitação.
var supportedCultures = new[] { "en-US", "fr-FR" }; var localizationOptions = new RequestLocalizationOptions() .SetDefaultCulture(supportedCultures[0]) .AddSupportedCultures(supportedCultures) .AddSupportedUICultures(supportedCultures); app.UseRequestLocalization(localizationOptions);
GraphQL é uma linguagem de consulta para APIs. A integração de uma API Web .NET com GraphQL permite uma recuperação de dados mais eficiente.
// Assume using a library like HotChocolate builder.Services .AddGraphQLServer() .AddQueryType<Query>(); app.MapGraphQL();
O monitoramento e a telemetria envolvem coletar, analisar e agir com base em dados sobre o desempenho e o uso do seu aplicativo.
// Assume using Application Insights builder.Services.AddApplicationInsightsTelemetry("YOUR_INSTRUMENTATION_KEY");
SignalR é uma biblioteca que simplifica a adição de funcionalidades da web em tempo real aos aplicativos. A funcionalidade da Web em tempo real é a capacidade de fazer com que o código do servidor envie conteúdo para clientes conectados instantaneamente, sem exigir que o servidor espere que um cliente solicite novos dados. SignalR é perfeito para desenvolver aplicativos de chat, painéis em tempo real e aplicativos web mais interativos.
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");
Integração em Program.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // Other configurations... app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/chathub"); }); }
O Entity Framework Core permite o mapeamento de relacionamentos complexos entre entidades, como um para um, um para muitos e muitos para muitos.
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; } }
Atributos de validação personalizados permitem definir sua lógica de validação para modelos de dados, estendendo os atributos de validação 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; } }
O padrão de opções do .NET oferece suporte a cenários de configuração complexos, incluindo objetos aninhados, listas e validação.
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"));
O monitoramento e a criação de perfil de um aplicativo podem identificar gargalos e ineficiências, essenciais para otimizar o desempenho.
app.UseMiniProfiler();
Aprimore a documentação da sua API integrando comentários XML à interface do usuário do Swagger, proporcionando uma experiência mais rica para os desenvolvedores que consomem sua API.
builder.Services.AddSwaggerGen(c => { var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); });
A globalização e a localização permitem que seu aplicativo suporte vários idiomas e culturas, tornando-o acessível a um público global.
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); app.UseRequestLocalization(app.Services.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);
Melhorar a segurança do seu aplicativo da web adicionando vários cabeçalhos HTTP pode proteger contra ataques e vulnerabilidades comuns.
app.UseHsts(); app.UseXContentTypeOptions(); app.UseReferrerPolicy(opts => opts.NoReferrer()); app.UseXXssProtection(options => options.EnabledWithBlockMode()); app.UseXfo(options => options.Deny());
Os sinalizadores de recursos permitem ativar e desativar recursos do seu aplicativo sem implantar novo código, facilitando testes e implementações.
// Using a library like Microsoft.FeatureManagement builder.Services.AddFeatureManagement();
O Blazor permite que você crie UIs da web interativas usando C# em vez de JavaScript. A integração do Blazor com a API da Web fornece uma experiência de desenvolvimento full-stack perfeita.
// In a Blazor Server app @code { private IEnumerable<WeatherForecast> forecasts; protected override async Task OnInitializedAsync() { forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast"); } }
A compactação de resposta pode reduzir o tamanho das respostas da API, melhorando os tempos de carregamento para clientes em redes lentas.
builder.Services.AddResponseCompression(options => { options.Providers.Add<GzipCompressionProvider>(); options.EnableForHttps = true; }); app.UseResponseCompression();
Obrigado por fazer parte da comunidade C#! Antes que partas:
Siga-nos: X | LinkedIn | Dev.to | Hashnode | Boletim informativo | Tumblr
Visite nossas outras plataformas: GitHub | Instagram | TikTok | Quora | Diariamente.dev
Também publicado aqui