ASP.NET Core中间件开发实战:自定义请求处理管道

ASP.NET Core中间件开发实战:自定义请求处理管道

【免费下载链接】aspnetcore dotnet/aspnetcore: 是一个 ASP.NET Core 应用程序开发框架的官方 GitHub 仓库,它包含了 ASP.NET Core 的核心源代码和技术文档。适合用于 ASP.NET Core 应用程序开发,特别是对于那些需要深入了解 ASP.NET Core 框架实现和技术的场景。特点是 ASP.NET Core 官方仓库、核心源代码、技术文档。 【免费下载链接】aspnetcore 项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore

引言:为什么需要自定义中间件?

在ASP.NET Core开发中,你是否遇到过这样的场景:需要统一处理所有请求的日志记录、需要实现自定义的身份验证逻辑、或者需要在特定条件下重写响应内容?这些需求正是中间件(Middleware)大显身手的时刻。

ASP.NET Core的请求处理管道(Request Pipeline)是一个强大的架构,它允许开发者通过中间件组件来灵活地处理HTTP请求和响应。本文将带你深入理解中间件的工作原理,并通过实战案例教你如何开发自定义中间件。

中间件核心概念解析

什么是中间件?

中间件是ASP.NET Core请求处理管道中的组件,它们按顺序处理HTTP请求和响应。每个中间件都可以:

  • 处理传入的请求
  • 将请求传递给管道中的下一个中间件
  • 处理传出的响应

中间件管道工作原理

mermaid

中间件的三种形式

形式描述适用场景
基于类的中间件完整的类实现复杂逻辑、可测试性要求高
内联中间件使用Use、Run、Map方法简单逻辑、快速原型
基于工厂的中间件实现IMiddleware接口依赖注入场景

实战一:创建基于类的自定义中间件

步骤1:定义中间件类

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace CustomMiddleware
{
    public class RequestLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<RequestLoggingMiddleware> _logger;

        public RequestLoggingMiddleware(
            RequestDelegate next, 
            ILogger<RequestLoggingMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // 记录请求开始时间
            var startTime = DateTime.UtcNow;
            
            _logger.LogInformation("开始处理请求: {Method} {Path}", 
                context.Request.Method, context.Request.Path);

            try
            {
                // 调用管道中的下一个中间件
                await _next(context);
                
                // 记录请求完成信息
                var duration = DateTime.UtcNow - startTime;
                _logger.LogInformation(
                    "请求处理完成: {Method} {Path} - 状态码: {StatusCode} - 耗时: {Duration}ms",
                    context.Request.Method, 
                    context.Request.Path, 
                    context.Response.StatusCode,
                    duration.TotalMilliseconds);
            }
            catch (Exception ex)
            {
                // 记录异常信息
                var duration = DateTime.UtcNow - startTime;
                _logger.LogError(ex, 
                    "请求处理失败: {Method} {Path} - 耗时: {Duration}ms - 错误: {ErrorMessage}",
                    context.Request.Method, 
                    context.Request.Path, 
                    duration.TotalMilliseconds,
                    ex.Message);
                
                // 重新抛出异常,让异常处理中间件处理
                throw;
            }
        }
    }
}

步骤2:创建扩展方法

using Microsoft.AspNetCore.Builder;

namespace CustomMiddleware
{
    public static class RequestLoggingMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestLogging(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestLoggingMiddleware>();
        }
    }
}

步骤3:注册中间件

Program.csStartup.cs中注册:

var builder = WebApplication.CreateBuilder(args);

// 添加日志服务
builder.Services.AddLogging();

var app = builder.Build();

// 配置中间件管道
app.UseRequestLogging(); // 自定义日志中间件
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

实战二:实现IMiddleware接口

对于需要依赖注入的复杂场景,可以实现IMiddleware接口:

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace CustomMiddleware
{
    public class CustomHeaderMiddleware : IMiddleware
    {
        private readonly IConfiguration _configuration;

        public CustomHeaderMiddleware(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            // 在处理请求前添加自定义头部
            context.Response.OnStarting(() =>
            {
                context.Response.Headers["X-Custom-Header"] = 
                    _configuration["CustomHeaderValue"] ?? "DefaultValue";
                context.Response.Headers["X-Request-ID"] = 
                    Guid.NewGuid().ToString();
                return Task.CompletedTask;
            });

            await next(context);
        }
    }
}

注册服务:

builder.Services.AddTransient<CustomHeaderMiddleware>();

使用中间件:

app.UseMiddleware<CustomHeaderMiddleware>();

实战三:条件性中间件处理

public class FeatureToggleMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IFeatureManager _featureManager;

    public FeatureToggleMiddleware(
        RequestDelegate next, 
        IFeatureManager featureManager)
    {
        _next = next;
        _featureManager = featureManager;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 检查特性开关
        if (await _featureManager.IsEnabledAsync("NewFeature"))
        {
            // 为新特性添加标记
            context.Items["UsingNewFeature"] = true;
            
            // 执行新特性逻辑
            await HandleNewFeature(context);
        }
        else
        {
            // 执行旧逻辑
            await _next(context);
        }
    }

    private async Task HandleNewFeature(HttpContext context)
    {
        // 新特性的处理逻辑
        context.Response.Headers["X-Feature-Version"] = "2.0";
        
        await _next(context);
        
        // 后处理逻辑
        if (context.Response.StatusCode == 200)
        {
            await context.Response.WriteAsync("\n<!-- New Feature Enabled -->");
        }
    }
}

中间件最佳实践

1. 错误处理策略

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ErrorHandlingMiddleware> _logger;

    public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "未处理的异常");
            
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var code = HttpStatusCode.InternalServerError;
        
        if (exception is NotFoundException) code = HttpStatusCode.NotFound;
        else if (exception is UnauthorizedException) code = HttpStatusCode.Unauthorized;
        
        var result = JsonSerializer.Serialize(new { error = exception.Message });
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)code;
        
        return context.Response.WriteAsync(result);
    }
}

2. 性能监控中间件

public class PerformanceMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<PerformanceMiddleware> _logger;

    public PerformanceMiddleware(RequestDelegate next, ILogger<PerformanceMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var stopwatch = Stopwatch.StartNew();
        
        await _next(context);
        
        stopwatch.Stop();
        
        if (stopwatch.ElapsedMilliseconds > 1000)
        {
            _logger.LogWarning("慢请求: {Path} 耗时 {Elapsed}ms", 
                context.Request.Path, stopwatch.ElapsedMilliseconds);
        }
    }
}

中间件管道配置模式

1. 条件性中间件注册

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseSwagger();
    app.UseSwaggerUI();
}

// 生产环境特定的中间件
if (app.Environment.IsProduction())
{
    app.UseHsts();
    app.UseResponseCompression();
}

// 通用中间件
app.UseRequestLogging();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();

2. 分支中间件管道

app.Map("/admin", adminApp =>
{
    adminApp.UseMiddleware<AdminAuthenticationMiddleware>();
    adminApp.UseMiddleware<AdminAuthorizationMiddleware>();
    adminApp.MapControllers();
});

app.Map("/api", apiApp =>
{
    apiApp.UseMiddleware<ApiVersionMiddleware>();
    apiApp.UseMiddleware<RateLimitingMiddleware>();
    apiApp.MapControllers();
});

测试自定义中间件

单元测试示例

[Test]
public async Task RequestLoggingMiddleware_LogsRequestInfo()
{
    // 安排
    var logger = new Mock<ILogger<RequestLoggingMiddleware>>();
    var next = new Mock<RequestDelegate>();
    var context = new DefaultHttpContext();
    context.Request.Method = "GET";
    context.Request.Path = "/test";
    
    var middleware = new RequestLoggingMiddleware(next.Object, logger.Object);
    
    // 执行
    await middleware.InvokeAsync(context);
    
    // 断言
    logger.Verify(x => x.Log(
        LogLevel.Information,
        It.IsAny<EventId>(),
        It.Is<It.IsAnyType>((v, t) => v.ToString().Contains("开始处理请求")),
        It.IsAny<Exception>(),
        It.IsAny<Func<It.IsAnyType, Exception, string>>()),
        Times.Once);
}

集成测试示例

[Fact]
public async Task CustomHeaderMiddleware_AddsCustomHeaders()
{
    // 安排
    var factory = new WebApplicationFactory<Program>();
    var client = factory.CreateClient();
    
    // 执行
    var response = await client.GetAsync("/api/test");
    
    // 断言
    response.Headers.Should().ContainKey("X-Custom-Header");
    response.Headers.Should().ContainKey("X-Request-ID");
}

常见问题与解决方案

问题1:中间件顺序错误

mermaid

问题2:中间件性能优化

public class OptimizedMiddleware
{
    private readonly RequestDelegate _next;
    private static readonly ConcurrentDictionary<string, bool> _pathCache = new();

    public OptimizedMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var path = context.Request.Path;
        
        // 使用缓存避免重复计算
        if (!_pathCache.TryGetValue(path, out var shouldProcess))
        {
            shouldProcess = ShouldProcessPath(path);
            _pathCache[path] = shouldProcess;
        }
        
        if (shouldProcess)
        {
            // 处理逻辑
            await ProcessRequest(context);
        }
        
        await _next(context);
    }
    
    private bool ShouldProcessPath(string path) => 
        path.StartsWith("/api/", StringComparison.OrdinalIgnoreCase);
}

总结

通过本文的实战指南,你应该已经掌握了ASP.NET Core中间件开发的核心技能:

  1. 理解中间件管道工作原理:掌握了请求处理的生命周期和中间件的执行顺序
  2. 创建各类中间件:学会了基于类、IMiddleware接口、内联等多种中间件实现方式
  3. 实现高级功能:掌握了条件处理、错误处理、性能监控等高级中间件技术
  4. 测试和调试:学会了如何对中间件进行单元测试和集成测试

中间件是ASP.NET Core架构的核心,通过自定义中间件,你可以实现各种强大的功能,从简单的日志记录到复杂的业务逻辑处理。记住中间件开发的最佳实践,确保你的中间件是高效、可靠且易于维护的。

现在,是时候将这些知识应用到你的项目中,构建出更加强大和灵活的ASP.NET Core应用程序了!

【免费下载链接】aspnetcore dotnet/aspnetcore: 是一个 ASP.NET Core 应用程序开发框架的官方 GitHub 仓库,它包含了 ASP.NET Core 的核心源代码和技术文档。适合用于 ASP.NET Core 应用程序开发,特别是对于那些需要深入了解 ASP.NET Core 框架实现和技术的场景。特点是 ASP.NET Core 官方仓库、核心源代码、技术文档。 【免费下载链接】aspnetcore 项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值