Skip to content

与 ASP.NET Core 深度集成 ​

🌐 在 Web 应用中充分发挥 MediatR 的优势


📖 概述 ​

ASP.NET Core 是 MediatR 最主要的应用场景。本章讲解如何在 Web API、MVC、Razor Pages 等应用中深度集成 MediatR。


🔧 自动扫描与注册 ​

基础配置 ​

csharp
// Program.cs
var builder = WebApplication.CreateBuilder(args);

// 添加控制器支持
builder.Services.AddControllers();

// 注册 MediatR(自动扫描当前程序集)
builder.Services.AddMediatR(cfg => 
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));

var app = builder.Build();
app.MapControllers();
app.Run();

多程序集扫描 ​

csharp
builder.Services.AddMediatR(cfg => {
    // 扫描多个程序集
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
    cfg.RegisterServicesFromAssembly(typeof(SharedLibraryMarker).Assembly);
    cfg.RegisterServicesFromAssembly(typeof(OrderHandlersMarker).Assembly);
});

自定义服务生命周期 ​

csharp
builder.Services.AddMediatR(cfg => {
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);
    cfg.Lifetime = ServiceLifetime.Scoped; // 默认 Transient
});

🎮 在控制器中使用 IMediator ​

RESTful API ​

csharp
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    private readonly IMediator _mediator;

    public OrdersController(IMediator mediator)
    {
        _mediator = mediator;
    }

    // GET: api/orders/{id}
    [HttpGet("{id}")]
    public async Task<ActionResult<OrderDto>> GetOrder(Guid id)
    {
        var query = new GetOrderQuery { OrderId = id };
        var order = await _mediator.Send(query);
        
        if (order == null)
            return NotFound();
        
        return Ok(order);
    }

    // POST: api/orders
    [HttpPost]
    public async Task<ActionResult<OrderResult>> CreateOrder([FromBody] CreateOrderCommand command)
    {
        var result = await _mediator.Send(command);
        return CreatedAtAction(nameof(GetOrder), new { id = result.OrderId }, result);
    }

    // PUT: api/orders/{id}
    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateOrder(Guid id, [FromBody] UpdateOrderCommand command)
    {
        if (id != command.OrderId)
            return BadRequest();

        await _mediator.Send(command);
        return NoContent();
    }

    // DELETE: api/orders/{id}
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteOrder(Guid id)
    {
        await _mediator.Send(new DeleteOrderCommand { OrderId = id });
        return NoContent();
    }
}

MVC 控制器 ​

csharp
public class HomeController : Controller
{
    private readonly IMediator _mediator;

    public HomeController(IMediator mediator)
    {
        _mediator = mediator;
    }

    public async Task<IActionResult> Index()
    {
        var orders = await _mediator.Send(new ListOrdersQuery());
        return View(orders);
    }

    [HttpPost]
    public async Task<IActionResult> Create(CreateOrderViewModel model)
    {
        if (!ModelState.IsValid)
            return View(model);

        var command = new CreateOrderCommand
        {
            ProductName = model.ProductName,
            Quantity = model.Quantity
        };

        await _mediator.Send(command);
        return RedirectToAction("Index");
    }
}

🛠️ 在中间件中使用 MediatR ​

请求日志中间件 ​

csharp
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, IMediator mediator)
    {
        var requestId = Guid.NewGuid();
        _logger.LogInformation("请求开始: {Method} {Path} [{RequestId}]", 
            context.Request.Method, 
            context.Request.Path, 
            requestId);

        try
        {
            await _next(context);
            
            _logger.LogInformation("请求完成: {StatusCode} [{RequestId}]", 
                context.Response.StatusCode, 
                requestId);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "请求失败: [{RequestId}]", requestId);
            throw;
        }
    }
}

// 注册中间件
app.UseMiddleware<RequestLoggingMiddleware>();

全局异常处理中间件 ​

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, IMediator mediator)
    {
        try
        {
            await _next(context);
        }
        catch (ValidationException ex)
        {
            _logger.LogWarning("验证失败: {Errors}", ex.Errors);
            
            context.Response.StatusCode = 400;
            await context.Response.WriteAsJsonAsync(new
            {
                errors = ex.Errors.Select(e => new
                {
                    field = e.PropertyName,
                    message = e.ErrorMessage
                })
            });
        }
        catch (NotFoundException ex)
        {
            _logger.LogWarning("资源未找到: {Message}", ex.Message);
            
            context.Response.StatusCode = 404;
            await context.Response.WriteAsJsonAsync(new { error = ex.Message });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "未处理的异常");
            
            context.Response.StatusCode = 500;
            await context.Response.WriteAsJsonAsync(new { error = "服务器内部错误" });
        }
    }
}

// 注册(放在管道前面)
app.UseMiddleware<ExceptionHandlingMiddleware>();

📄 在 Razor Pages 中使用 ​

csharp
// Pages/Orders/Index.cshtml.cs
public class IndexModel : PageModel
{
    private readonly IMediator _mediator;

    public List<OrderDto> Orders { get; set; }

    public IndexModel(IMediator mediator)
    {
        _mediator = mediator;
    }

    public async Task OnGetAsync()
    {
        Orders = await _mediator.Send(new ListOrdersQuery());
    }
}

// Pages/Orders/Create.cshtml.cs
public class CreateModel : PageModel
{
    private readonly IMediator _mediator;

    [BindProperty]
    public CreateOrderViewModel Order { get; set; }

    public CreateModel(IMediator mediator)
    {
        _mediator = mediator;
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
            return Page();

        var command = new CreateOrderCommand
        {
            ProductName = Order.ProductName,
            Quantity = Order.Quantity
        };

        await _mediator.Send(command);
        return RedirectToPage("./Index");
    }
}

📚 结合 Swagger/OpenAPI 文档 ​

安装 NuGet 包 ​

bash
dotnet add package Swashbuckle.AspNetCore

配置 Swagger ​

csharp
// Program.cs
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo 
    { 
        Title = "订单 API", 
        Version = "v1",
        Description = "基于 MediatR 的订单管理系统 API"
    });

    // 包含 XML 注释
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);
});

// 启用 Swagger UI
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "订单 API v1");
        c.RoutePrefix = string.Empty; // 在根路径显示
    });
}

为命令添加文档注释 ​

csharp
/// <summary>
/// 创建订单命令
/// </summary>
public class CreateOrderCommand : IRequest<OrderResult>
{
    /// <summary>
    /// 产品名称
    /// </summary>
    /// <example>iPhone 15 Pro</example>
    public string ProductName { get; set; }

    /// <summary>
    /// 数量
    /// </summary>
    /// <example>2</example>
    public int Quantity { get; set; }

    /// <summary>
    /// 单价
    /// </summary>
    /// <example>7999.00</example>
    public decimal Price { get; set; }
}

🔐 身份验证与授权 ​

在 Handler 中获取用户信息 ​

csharp
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CreateOrderHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<OrderResult> Handle(CreateOrderCommand request, CancellationToken ct)
    {
        // 获取当前用户
        var userId = _httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier);
        var userEmail = _httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.Email);

        // 使用用户信息
        var order = new Order
        {
            ProductName = request.ProductName,
            CreatedBy = userId,
            CustomerEmail = userEmail
        };

        await _dbContext.Orders.AddAsync(order, ct);
        await _dbContext.SaveChangesAsync(ct);

        return new OrderResult { OrderId = order.Id };
    }
}

// 注册 HttpContextAccessor
builder.Services.AddHttpContextAccessor();

基于角色的授权 ​

csharp
[Authorize(Roles = "Admin,Manager")]
[HttpPost]
public async Task<ActionResult<OrderResult>> CreateOrder([FromBody] CreateOrderCommand command)
{
    var result = await _mediator.Send(command);
    return Ok(result);
}

🎯 最佳实践 ​

✅ 推荐做法 ​

csharp
// 1. 控制器保持简洁,只负责 HTTP 层面
[HttpPost]
public async Task<ActionResult<OrderResult>> CreateOrder([FromBody] CreateOrderCommand command)
{
    var result = await _mediator.Send(command);
    return CreatedAtAction(nameof(GetOrder), new { id = result.OrderId }, result);
}

// 2. 业务逻辑在 Handler 中
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
    public async Task<OrderResult> Handle(CreateOrderCommand request, CancellationToken ct)
    {
        // 所有业务逻辑在这里
    }
}

// 3. 使用管道行为处理横切关注点
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

// 4. 全局异常处理中间件
app.UseMiddleware<ExceptionHandlingMiddleware>();

❌ 避免的做法 ​

csharp
// 1. 不要在控制器中包含业务逻辑
[HttpPost]
public async Task<ActionResult> CreateOrder([FromBody] CreateOrderCommand command)
{
    // ❌ 错误:业务逻辑应该在 Handler 中
    var order = new Order { /* ... */ };
    await _dbContext.Orders.AddAsync(order);
    await _emailService.SendConfirmation(order);
    return Ok();
}

// 2. 不要直接注入 Repository 到控制器
public class OrdersController : ControllerBase
{
    private readonly IOrderRepository _repo; // ❌ 应该通过 Handler

    public OrdersController(IOrderRepository repo)
    {
        _repo = repo;
    }
}

// 3. 不要忘记异步
[HttpPost]
public ActionResult<OrderResult> CreateOrder([FromBody] CreateOrderCommand command) // ❌ 缺少 async
{
    return _mediator.Send(command); // ❌ 应该 await
}

📊 性能优化 ​

1. 启用响应缓存 ​

csharp
[HttpGet("{id}")]
[ResponseCache(Duration = 60)] // 缓存 60 秒
public async Task<ActionResult<OrderDto>> GetOrder(Guid id)
{
    var query = new GetOrderQuery { OrderId = id };
    var order = await _mediator.Send(query);
    return Ok(order);
}

2. 使用输出缓存(.NET 7+) ​

csharp
app.UseOutputCache();

[HttpGet]
[OutputCache(Duration = 60)]
public async Task<ActionResult<List<OrderDto>>> ListOrders()
{
    var orders = await _mediator.Send(new ListOrdersQuery());
    return Ok(orders);
}

3. 最小 API(.NET 6+) ​

csharp
// 更轻量的 API 定义
app.MapGet("/api/orders/{id}", async (Guid id, IMediator mediator) =>
{
    var order = await mediator.Send(new GetOrderQuery { OrderId = id });
    return order is null ? Results.NotFound() : Results.Ok(order);
});

app.MapPost("/api/orders", async (CreateOrderCommand command, IMediator mediator) =>
{
    var result = await mediator.Send(command);
    return Results.Created($"/api/orders/{result.OrderId}", result);
});

🎓 总结 ​

核心价值 ​

✅ 解耦: 控制器不依赖具体服务
✅ 测试友好: Handler 易于单元测试
✅ 一致性: 统一的请求处理模式
✅ 扩展性: 管道行为自动应用

关键要点 ​

  1. 控制器只负责 HTTP 层面
  2. 业务逻辑在 Handler 中
  3. 使用管道行为处理横切关注点
  4. 全局异常处理中间件
  5. 集成 Swagger 提供 API 文档

💡 提示: ASP.NET Core + MediatR 是企业级应用的最佳组合!

Released under the CC BY-SA 4.0 License.