????? ??????? Middleware ????????? ASP.NET Core-??
David Shergilashvili
Next-Gen CTO | Tech Leader | Software Development & Enterprise Solutions Architect | Cloud & DevOps Strategist | AI/ML Integration Specialist | Technical Community Contributor
ASP.NET Core 9-?? ???-???? ???????????????? ??????????? Middleware ???????. ?? ???? ?????????, ??????? ?????????? ???????? ??????? HTTP ?????????? ?? ???????? ??????? ?? ???????????? ?????????? ??????????.
???????? ??? ??????, Middleware ???? ?????????? ????? ??????, ??????? "?????????" ???? ???????? - ??? ????? ??????? ?????????? ?? ??????????, ????? ????????? ????? ????? ??????? ???????? ?????????.
????? ???? ?? ?????????????? ????????????, ??? ?????? ????? ????????? ????????, ???????????? ?????????, ?? ?????????? ??????????. Middleware-?? ?????? ?????????? ?? ??????? ?????? ??????? endpoint-?? ???-?????. Middleware ?????????? ????????, ?? ?????? ?????? ???????? ?? ??????????? ????? ??????????????.
Middleware: ?? ???? ?? ????? ????????
Middleware ASP.NET Core-?? ???? ?????????? ??????????, ??????? ??????? HTTP ??????????? ??????????? ????????. ???? ??????? ???????? ?????? ????????, ????? ???????? ???????? ????? ?? ?????? ?????????? ?????? ?????????? ?? ????? ????????? ??????????.
???????????? ?????????, ????? ???????? Middleware ???? ????, ??????? ????? ???????? (HTTP ?????????), ????????? ??? ?? ????????? ?????? ?????. ???? ???? ???????? ??????? ???????? (HTTP ??????).
??????? ?????????? ????, ??? ?? ??????? ??? ???????????? ???????:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// ??????? Middleware
app.Use(async (context, next) =>
{
Console.WriteLine("Middleware 1: ???????? ?????????");
await next.Invoke(); // ????????? ??????? ?????? Middleware-?
Console.WriteLine("Middleware 1: ?????? ??????");
});
// ????? Middleware
app.Use(async (context, next) =>
{
Console.WriteLine("Middleware 2: ???????? ?????????");
await next.Invoke(); // ????????? ??????? ?????? Middleware-?
Console.WriteLine("Middleware 2: ?????? ??????");
});
// ???? Middleware - ???????? ?????????
app.Run(async context =>
{
await context.Response.WriteAsync("Hello World!");
Console.WriteLine("??????? Middleware: ?????? ??????");
});
app.Run();
?? ????? ???????????? ???????? ??????????:
Middleware 1: ???????? ?????????
Middleware 2: ???????? ?????????
??????? Middleware: ?????? ??????
Middleware 2: ?????? ??????
Middleware 1: ?????? ??????
?????? ?? "????????" ??????? ???????? ?????, ??????? ????? Middleware-?, ??????? ?????? ?? ?????? ?????? ???????? ????? ????, ????? ???????????????.
????????????? ???????????? app.Use() ?? app.Run() ???????? ?????. Use ?????? ?????????? ???????? ??????????? ????????? ?????? Middleware-??, ???? Run ????????? ????????? - ?? "???????????" Middleware-?.
????? ???????? ???????? Middleware?
Middleware-?? ?????? ????????? ??? ????:
????, ?????? Middleware, ??????? ?????????? ???????? URL-???? ????????? ?????????? ??????? (???., ???).
?????? Middleware-?? ??????:
public class LanguageMiddleware
{
private readonly RequestDelegate _next;
public LanguageMiddleware(RequestDelegate next)
{
_next = next; // ???????? ?????? Middleware-?? ???????????
}
public async Task InvokeAsync(HttpContext context)
{
// ?????? ???? ????????? URL-????: ?lang=ka
string lang = context.Request.Query["lang"];
if (!string.IsNullOrEmpty(lang))
{
try
{
// ?????? ???????? ?????????????
var culture = new System.Globalization.CultureInfo(lang);
System.Globalization.CultureInfo.CurrentCulture = culture;
System.Globalization.CultureInfo.CurrentUICulture = culture;
Console.WriteLine($"??? ????????: {lang}");
}
catch (Exception)
{
Console.WriteLine($"???????? ???? ????: {lang}");
}
}
// ????????? ????????? ?????? Middleware-?
await _next(context);
}
}
????, ??? ???????????? ?? Middleware-?? ??????????, ???????? Extension ??????:
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseLanguage(this IApplicationBuilder app)
{
return app.UseMiddleware<LanguageMiddleware>();
}
}
?? ??????????? ????? ???????????:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseLanguage(); // ???????? ????? ???? Middleware-?
app.MapGet("/", () => "Hello World!");
app.Run();
???? ????????? URL-?? ????????? ????????? ?lang=ka ?? ????? ????????? ?????? ????? ??????.
ASP.NET Core 9-?? ????????? Middleware-?? ??????
ASP.NET Core 9-? ????????? ????????????? ?????? ????????? Middleware-?? ????????????. ??, ????????? ???????:
1. Keyed DI ??????????
???? ????????? ??????? ????????? ????????? Middleware-?? ????????????? ?? InvokeAsync ???????, ??? ???? ???? ??????? ????.
// ?????????? ???????????
builder.Services.AddKeyedSingleton<IMyService, MyServiceImpl1>("service1");
builder.Services.AddKeyedSingleton<IMyService, MyServiceImpl2>("service2");
// Middleware-?? ??????????
public class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly IMyService _service;
public MyMiddleware(RequestDelegate next, [FromKeyedServices("service1")] IMyService service)
{
_next = next;
_service = service;
}
public async Task InvokeAsync(HttpContext context, [FromKeyedServices("service2")] IMyService scopedService)
{
// ????? ???????? ??????????
await _next(context);
}
}
?? ?????????? ???????? ???? ??????????? ????????? ????????????? ??????????? ?????????? ??????????.
2. ExceptionHandler-?? ????????????
ASP.NET Core 9-?? ????????? ???????????? ????? ???????? ???????????? ?????????? HTTP ?????? ????:
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = ex => ex switch
{
TimeoutException => StatusCodes.Status504GatewayTimeout,
UnauthorizedAccessException => StatusCodes.Status401Unauthorized,
_ => StatusCodes.Status500InternalServerError
}
});
?? ?????????? ???? ?????? HTTP ???????? ?????????? ?????????????? ?? API-?? ???????????? ?????????????.
3. StaticFiles-?? ????????????: MapStaticAssets
????????? ???????? ??????????? ????? ???? ???????? ????? ???????:
// ????? ???
// app.UseStaticFiles();
// ????? ???
app.MapStaticAssets();
????? ?????? ??????????? ????????????:
4. ?????????? ?????????? ????????
ASP.NET Core 9-?? ????????? ????????? ?????????? ????????? ?????????? ???????????????? ?? ???????????????:
// ?????????????? ?????????? ????????
app.MapGet("/health", () => "Healthy").DisableHttpMetrics();
// ?? Middleware-?? ?????????? ???????????????
app.Use(async (context, next) =>
{
if (context.Request.Headers.ContainsKey("x-disable-metrics"))
{
var metricsFeature = context.Features.Get<IHttpMetricsTagsFeature>();
if (metricsFeature != null)
{
metricsFeature.MetricsDisabled = true;
}
}
await next(context);
});
?? ????????????? ??????????? ?????? ?????????? ??????????, ????? ????? ????????? ???????? ????????? ??????? ???????? ????.
?????????? ??????????
???? ?????? ????????? ??????? ????????, ???????? ??????????, ????? ????????? ??????? ?????????? Middleware-?? ???????????.
???????? 1: ??????????? ???????? Correlation ID-??
????????????, ??? ?????? ??????????????? ???????, ????? ???? ???????? ????????? ???????? ???????. ????? ?????????? ??????? ?????? ????? ?????????? Correlation ID (?????????? ??????????????).
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<LoggingMiddleware> _logger;
public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// ?????? ?? ?????? Correlation ID-?
string correlationId = context.Request.Headers["X-Correlation-ID"];
if (string.IsNullOrEmpty(correlationId))
{
correlationId = Guid.NewGuid().ToString();
context.Request.Headers["X-Correlation-ID"] = correlationId;
}
// ???????? ????? ID-? ????????
context.Response.OnStarting(() => {
context.Response.Headers["X-Correlation-ID"] = correlationId;
return Task.CompletedTask;
});
// ??????? ????????? ????????
_logger.LogInformation("[{CorrelationId}] ?????? {Method} {Path}",
correlationId, context.Request.Method, context.Request.Path);
// ????? ????????
var stopwatch = Stopwatch.StartNew();
try
{
// ?????????? ????????? ???????????
await _next(context);
// ??????? ?????????? ??????????
_logger.LogInformation("[{CorrelationId}] ???????? {Method} {Path} ???????? {StatusCode} ({Elapsed}ms)",
correlationId, context.Request.Method, context.Request.Path,
context.Response.StatusCode, stopwatch.ElapsedMilliseconds);
}
catch (Exception ex)
{
// ??????? ????????
_logger.LogError(ex, "[{CorrelationId}] ??????? {Method} {Path}",
correlationId, context.Request.Method, context.Request.Path);
throw; // ???????? ????????? ?????? Middleware-?
}
}
}
?? ????????:
?? ?????????? ???????? ??????? ???????? ????????? ????????? ??????? ?? ????????????, ?? ?????.
???????? 2: IP ????????? ????????????????
?? ?????? Middleware-?, ??????? ??????? ?? ?????? ?????????? IP ?????????? ????????.
public class IpFilterMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<IpFilterMiddleware> _logger;
private readonly HashSet<string> _allowedIps;
public IpFilterMiddleware(RequestDelegate next, ILogger<IpFilterMiddleware> logger, IConfiguration config)
{
_next = next;
_logger = logger;
// ?????????? ?????????? IP-??? ???????????????
_allowedIps = new HashSet<string>(
config.GetSection("Security:AllowedIps").Get<string[]>() ?? Array.Empty<string>()
);
_logger.LogInformation("IP ????????? ????????. ??????????? IP-???: {IpCount}", _allowedIps.Count);
}
public async Task InvokeAsync(HttpContext context)
{
var ipAddress = context.Connection.RemoteIpAddress?.ToString();
// ?? IP-???? ??? ????????, ????? ????????? ???????
if (_allowedIps.Count > 0 && !string.IsNullOrEmpty(ipAddress))
{
if (!_allowedIps.Contains(ipAddress))
{
_logger.LogWarning("???????? ?????????? ??????????????? IP-???: {IpAddress}", ipAddress);
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync("?????? ??????????.");
return; // ????????? ??????? ??
}
}
// ?? IP ????????????, ??????????
await _next(context);
}
}
?? Middleware-??:
?? ????????? ?????? ??????????? ??????????????? ????????? ?? API-???? ?????????.
???????? 3: ???????????????? ?????????? ??????????
??????????????? ???????????? ??????? ???????, ?????? ???????????? (tenant) ???????? ?????????. ?? ???????? ???????????? ??????????, URL-?? ????????? ?? ???? ??????????.
public class TenantMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<TenantMiddleware> _logger;
public TenantMiddleware(RequestDelegate next, ILogger<TenantMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context, ITenantService tenantService)
{
// ???????? ?????????? URL-?? ??????? ???????????
// ???.: /tenant1/api/products
var path = context.Request.Path.Value ?? "";
var segments = path.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (segments.Length > 0)
{
string potentialTenant = segments[0];
if (await tenantService.TenantExistsAsync(potentialTenant))
{
// ??????? ????????, ???????? ??????????
var tenant = await tenantService.GetTenantByIdAsync(potentialTenant);
context.Items["CurrentTenant"] = tenant;
// ??????????? ???? - ????? ???????? ??????
context.Request.Path = path.Substring(potentialTenant.Length + 1);
_logger.LogInformation("??????? ?????????????: {TenantId}", tenant.Id);
}
}
// ?? ??????? ??? ???????????, ???????????? ????????????
if (!context.Items.ContainsKey("CurrentTenant"))
{
context.Items["CurrentTenant"] = await tenantService.GetDefaultTenantAsync();
_logger.LogInformation("??????????? ???????????? ???????");
}
await _next(context);
}
}
?? Middleware-??:
??? ???????? ????????????? ?????????????????? ?????????? ????????????????, ????????? ???????? ?? ?????????????? ??????????? ???? ?????????? ?????????.
????????? ?????????? ?? ???????????
Middleware-????? ?????????? ???????? ????????? ????????????? ????????, ??????? ???? ???????????????:
1. ????? ?????????????
Middleware-???? ????????????? ?????????? ??????????????. ?? ????????? ????:
??????????? ????????????? ???????? ??? ??????????????:
app.UseExceptionHandler("/error");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
2. ??????????????? ?????????
3. ???????????