You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何消除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 04:31:02