如何消除Minimal API代码重复?实现基础端点类并静态引用Mediatr
减少Minimal API重复代码并封装MediatR使用方案
一、实现静态方式获取MediatR实例
无需在每个端点方法中注入IMediator,可以通过HttpContext的服务容器直接获取实例。我们封装一个静态工具类简化操作:
public static class MediatorHelper { public static IMediator GetMediator(this HttpContext context) { return context.RequestServices.GetRequiredService<IMediator>(); } }
之后在端点方法中,只需通过HttpContext就能获取MediatR实例,不用再将其作为参数注入。
二、封装基础端点配置与逻辑
通过创建通用扩展方法,把重复的端点配置(如WithName、WithOpenApi、ProducesValidationProblem)和请求处理逻辑统一封装,减少重复代码。
1. 通用端点配置扩展
封装命令/查询类端点的重复元数据配置:
public static class EndpointConfigurationExtensions { // POST/PUT命令类端点通用配置 public static RouteHandlerBuilder ConfigureCommandEndpoint<TCommand>(this RouteHandlerBuilder builder, string endpointName, Type responseType, int successStatusCode = StatusCodes.Status200OK) { return builder .AddEndpointFilter<ValidationFilter<TCommand>>() .WithName(endpointName) .Produces(successStatusCode, responseType) .ProducesValidationProblem() .WithOpenApi(); } // GET查询类端点通用配置 public static RouteHandlerBuilder ConfigureQueryEndpoint(this RouteHandlerBuilder builder, string endpointName, Type responseType) { return builder .WithName(endpointName) .Produces(StatusCodes.Status200OK, responseType) .WithOpenApi(); } }
2. 通用请求处理逻辑封装
封装MediatR请求发送和结果返回的通用流程:
public static class EndpointHandlerHelpers { // 处理带参数的命令请求,支持自定义成功结果 public static async Task<IResult> HandleCommandAsync<TCommand, TResponse>( TCommand command, HttpContext context, CancellationToken cancellationToken, Func<TResponse, IResult>? successResultFactory = null) { var mediator = context.GetMediator(); var response = await mediator.Send(command, cancellationToken); return successResultFactory?.Invoke(response) ?? Results.Ok(response); } // 处理无参数的查询请求 public static async Task<IResult> HandleQueryAsync<TQuery, TResponse>( HttpContext context, CancellationToken cancellationToken) where TQuery : new() { var mediator = context.GetMediator(); var response = await mediator.Send(new TQuery(), cancellationToken); return Results.Ok(response); } // 处理带参数的查询请求 public static async Task<IResult> HandleQueryAsync<TQuery, TResponse>( TQuery query, HttpContext context, CancellationToken cancellationToken) { var mediator = context.GetMediator(); var response = await mediator.Send(query, cancellationToken); return Results.Ok(response); } }
三、重构你的ArticleEndpoints
基于上述封装,重构后的端点代码将大幅简化:
public static class ArticleEndpoints { public static void MapArticleEndpoints(this WebApplication app) { var articles = app.MapGroup("Articles"); articles.MapPost(nameof(CreateAsync), CreateAsync) .ConfigureCommandEndpoint<CreateArticleCommand, Guid>(nameof(CreateAsync), typeof(Guid), StatusCodes.Status201Created); articles.MapPut(nameof(AddTagToArticleAsync), AddTagToArticleAsync) .ConfigureCommandEndpoint<AddTagToArticleCommand, ArticleGetByIdDto>(nameof(AddTagToArticleAsync), typeof(ArticleGetByIdDto)); articles.MapGet(nameof(GetAllAsync), GetAllAsync) .ConfigureQueryEndpoint(nameof(GetAllAsync), typeof(IReadOnlyCollection<ArticleGetAllDto>)); articles.MapGet(nameof(GetByIdAsync), GetByIdAsync) .ConfigureQueryEndpoint(nameof(GetByIdAsync), typeof(TagGetByIdDto)); } public static async Task<IResult> CreateAsync([FromBody] CreateArticleCommand command, HttpContext context, CancellationToken cancellationToken) { return await EndpointHandlerHelpers.HandleCommandAsync<CreateArticleCommand, Guid>( command, context, cancellationToken, id => Results.Created(nameof(CreateAsync), new { id })); } public static async Task<IResult> AddTagToArticleAsync([FromBody] AddTagToArticleCommand command, HttpContext context, CancellationToken cancellationToken) { return await EndpointHandlerHelpers.HandleCommandAsync<AddTagToArticleCommand, ArticleGetByIdDto>(command, context, cancellationToken); } public static async Task<IResult> GetAllAsync(HttpContext context, CancellationToken cancellationToken) { return await EndpointHandlerHelpers.HandleQueryAsync<GetAllArticlesQuery, IReadOnlyCollection<ArticleGetAllDto>>(context, cancellationToken); } public static async Task<IResult> GetByIdAsync([FromBody] GetArticleByIdIncludeTagsQuery query, HttpContext context, CancellationToken cancellationToken) { return await EndpointHandlerHelpers.HandleQueryAsync<GetArticleByIdIncludeTagsQuery, TagGetByIdDto>(query, context, cancellationToken); } }
四、额外优化:内联端点逻辑
如果希望进一步精简代码,可直接在Map时内联调用通用处理方法,省去单独的方法定义:
articles.MapPost(nameof(CreateAsync), async ([FromBody] CreateArticleCommand cmd, HttpContext ctx, CancellationToken ct) => await EndpointHandlerHelpers.HandleCommandAsync(cmd, ctx, ct, id => Results.Created(nameof(CreateAsync), new { id }))) .ConfigureCommandEndpoint<CreateArticleCommand, Guid>(nameof(CreateAsync), typeof(Guid), StatusCodes.Status201Created);
内容的提问来源于stack exchange,提问作者Baran
相关产品推荐
相关产品推荐

