ASP.NET Core Web API para .NET Senior
📋 Tópicos Essenciais
1. Configuração e Setup
Program.cs (Minimal API)
csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();Startup.cs (Traditional)
csharp
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<IUserService, UserService>();
services.AddScoped<IUserRepository, UserRepository>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}2. Controller Design
RESTful Controller
csharp
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
private readonly ILogger<UsersController> _logger;
public UsersController(IUserService userService, ILogger<UsersController> logger)
{
_userService = userService;
_logger = logger;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<UserDto>>> GetUsers([FromQuery] UserQueryParameters parameters)
{
try
{
var users = await _userService.GetUsersAsync(parameters);
return Ok(users);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting users");
return StatusCode(500, "Internal server error");
}
}
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
var user = await _userService.GetUserByIdAsync(id);
if (user == null)
return NotFound();
return Ok(user);
}
[HttpPost]
public async Task<ActionResult<UserDto>> CreateUser([FromBody] CreateUserRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var user = await _userService.CreateUserAsync(request);
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateUser(int id, [FromBody] UpdateUserRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var success = await _userService.UpdateUserAsync(id, request);
if (!success)
return NotFound();
return NoContent();
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteUser(int id)
{
var success = await _userService.DeleteUserAsync(id);
if (!success)
return NotFound();
return NoContent();
}
}3. Model Validation
Data Annotations
csharp
public class CreateUserRequest
{
[Required]
[StringLength(100, MinimumLength = 2)]
public string Name { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, MinimumLength = 6)]
public string Password { get; set; }
[Range(18, 120)]
public int Age { get; set; }
}Custom Validation
csharp
public class UniqueEmailAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var email = value as string;
if (string.IsNullOrEmpty(email))
return ValidationResult.Success;
var userService = (IUserService)validationContext.GetService(typeof(IUserService));
var isUnique = userService.IsEmailUniqueAsync(email).Result;
return isUnique ? ValidationResult.Success : new ValidationResult("Email already exists");
}
}4. Authentication e Authorization
JWT Authentication
csharp
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
}
}Authorization Policies
csharp
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Admin"));
options.AddPolicy("MinimumAge", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c => c.Type == "Age" &&
int.Parse(c.Value) >= 18)));
});
}
}5. Middleware Customizado
Logging Middleware
csharp
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var startTime = DateTime.UtcNow;
await _next(context);
var elapsed = DateTime.UtcNow - startTime;
_logger.LogInformation(
"Request {method} {url} => {statusCode} ({elapsed}ms)",
context.Request.Method,
context.Request.Path,
context.Response.StatusCode,
elapsed.TotalMilliseconds);
}
}Exception Handling Middleware
csharp
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An unhandled exception occurred");
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
var response = new
{
error = "An error occurred while processing your request",
details = exception.Message
};
await context.Response.WriteAsJsonAsync(response);
}
}6. API Versioning
Versioning Configuration
csharp
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
});
services.AddVersionedApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
}
}Versioned Controller
csharp
[ApiController]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet]
[MapToApiVersion("1.0")]
public async Task<ActionResult<IEnumerable<UserDto>>> GetUsersV1()
{
return Ok(await _userService.GetUsersAsync());
}
[HttpGet]
[MapToApiVersion("2.0")]
public async Task<ActionResult<IEnumerable<UserDtoV2>>> GetUsersV2()
{
return Ok(await _userService.GetUsersV2Async());
}
}7. Response Caching
Controller Level Caching
csharp
[ApiController]
[Route("api/[controller]")]
[ResponseCache(Duration = 300, Location = ResponseCacheLocation.Any)]
public class ProductsController : ControllerBase
{
[HttpGet]
[ResponseCache(Duration = 600)]
public async Task<ActionResult<IEnumerable<ProductDto>>> GetProducts()
{
return Ok(await _productService.GetProductsAsync());
}
}Distributed Caching
csharp
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedRedisCache(options =>
{
options.Configuration = Configuration.GetConnectionString("Redis");
});
}
}
public class CachedProductService : IProductService
{
private readonly IProductService _productService;
private readonly IDistributedCache _cache;
public CachedProductService(IProductService productService, IDistributedCache cache)
{
_productService = productService;
_cache = cache;
}
public async Task<IEnumerable<ProductDto>> GetProductsAsync()
{
var cacheKey = "products_all";
var cached = await _cache.GetStringAsync(cacheKey);
if (cached != null)
return JsonSerializer.Deserialize<IEnumerable<ProductDto>>(cached);
var products = await _productService.GetProductsAsync();
var serialized = JsonSerializer.Serialize(products);
await _cache.SetStringAsync(cacheKey, serialized, new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
});
return products;
}
}8. Rate Limiting
Rate Limiting Configuration
csharp
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: context.User.Identity?.Name ?? context.Request.Headers.Host.ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
}));
});
}
}Controller Level Rate Limiting
csharp
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet]
[EnableRateLimiting("fixed")]
public async Task<ActionResult<IEnumerable<UserDto>>> GetUsers()
{
return Ok(await _userService.GetUsersAsync());
}
}9. Health Checks
Health Check Configuration
csharp
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks()
.AddDbContextCheck<ApplicationDbContext>()
.AddRedis(Configuration.GetConnectionString("Redis"))
.AddUrlGroup(new Uri("https://api.external.com"), "external-api");
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
});
}
}10. Testing
Integration Tests
csharp
public class UsersControllerTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public UsersControllerTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Fact]
public async Task GetUsers_ReturnsSuccessResult()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("/api/users");
response.EnsureSuccessStatusCode();
Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString());
}
}📚 Recursos Adicionais
Livros Recomendados
- "ASP.NET Core in Action" - Andrew Lock
- "Building Microservices" - Sam Newman
Artigos e Blogs
- Microsoft ASP.NET Core Documentation
- Andrew Lock's Blog
- ASP.NET Core GitHub Repository
⚠️ Pontos de Atenção
- Security: Sempre valide inputs e use HTTPS
- Performance: Use async/await adequadamente
- Error Handling: Implemente logging e tratamento de exceções
- Versioning: Planeje versionamento da API
- Documentation: Mantenha documentação atualizada