SPMS_API/SPMS.API/Program.cs
SEAN 58b94c6298 feat: API Rate Limiting 및 Swagger UI 구현 (#30)
- ASP.NET Core 내장 Rate Limiting (FixedWindow, IP 기반 분당 100회)
- 한도 초과 시 HTTP 429 + ApiResponse(에러코드 106) 반환
- Swashbuckle.AspNetCore 6.9.0 기반 Swagger UI 추가
- 도메인별 API 문서 그룹 (all, public, auth 등 10개)
- JWT Bearer 인증 UI (Authorize 버튼)
- X-Service-Code/X-API-KEY 커스텀 헤더 자동 표시 필터
- Microsoft.AspNetCore.OpenApi 제거 (Swashbuckle과 호환 충돌)

Closes #30
2026-02-09 17:11:46 +09:00

55 lines
1.8 KiB
C#

using System.Threading.RateLimiting;
using Serilog;
using SPMS.API.Extensions;
using SPMS.Application;
using SPMS.Domain.Common;
using SPMS.Infrastructure;
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
WebRootPath = Environment.GetEnvironmentVariable("ASPNETCORE_WEBROOT")
?? "wwwroot"
});
// ===== 1. Serilog =====
builder.Host.UseSerilog((context, config) =>
config.ReadFrom.Configuration(context.Configuration));
// ===== 2. Services (DI) =====
builder.Services.AddApplication();
builder.Services.AddInfrastructure(builder.Configuration);
// ===== 3. Presentation =====
builder.Services.AddControllers();
builder.Services.AddSwaggerDocumentation();
builder.Services.AddJwtAuthentication(builder.Configuration);
builder.Services.AddAuthorizationPolicies();
// ===== 4. Rate Limiting =====
builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
options.OnRejected = async (context, cancellationToken) =>
{
context.HttpContext.Response.ContentType = "application/json";
var response = ApiResponse.Fail(
ErrorCodes.LimitExceeded, "요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요.");
await context.HttpContext.Response.WriteAsJsonAsync(response, cancellationToken);
};
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown",
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
}));
});
var app = builder.Build();
// ===== 5. Middleware Pipeline =====
app.UseMiddlewarePipeline();
app.Run();