1

根据MediatR的Contract Messages自动生成Minimal WebApi接口 - 小庄

 1 year ago
source link: https://www.cnblogs.com/xiaozhuang/p/16922032.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

根据MediatR的Contract Messages自动生成Minimal WebApi接口

大家好,我是失业在家,正在找工作的博主Jerry。今天给大家介绍一个能大大减少ASP.Net Minimal WebApi编码量的方法。

我们一般会把微服务的VO和DTO封装成消息类,并作为WebApi的Request和Response参数进行网络传递。

如果使用MediatR,我们封装的消息类就要实现 MediatR Contract 接口 IRequest<> 或者INotification,  例如我的代码如下:

namespace MediatRApplication.CategoryCRUD
{
    public class CreateCategory : IRequest<CreateCategoryResult>
    {
        public string Message { get; set; }
    }
    public class CreateCategoryResult
    {
        public string Message { get; set; }
    }

    public class ReadCategory : IRequest<ReadCategoryResult>
    {
        public string Message { get; set; }
    }
    public class ReadCategoryResult
    {
        public string Message { get; set; }
    }

    public class UpdateCategory : IRequest<UpdateCategoryResult>
    {
        public string Message { get; set; }
    }
    public class UpdateCategoryResult
    {
        public string Message { get; set; }
    }

    public class DeleteCategory : IRequest
    {
        public string Message { get; set; }
    }
}

如上代码是对Category业务实体进行CRUD操作封装的DTO消息类,每个消息类都实现了MediatR的IRequest接口。有了消息类,就可以对每个消息类编写处理器(Handler),以实现业务功能。

有了消息类,就需要为每个消息类创建WebApi接口,以实现消息的Request和Response。WebAPI接口中没有业务逻辑,只需要调用MediatR的Send方法将消息类发送给Handler即可。

但是,由于消息类比较多,一个一个创建WebApi接口是一件费时费力,并且容易出错的事情。作为一个架构师,是无法忍受程序员们干这些出力不讨好的事情的。

所以,为了项目,为了大家的Work Life Banlance, 我把创建WebApi这件事情减少成了一行代码。是的,你没看错,就是只要一行代码:

app.MapMediatorWebAPIs(typeof(CreateCategory).Assembly);

只要在ASP.Net Minimal API 项目的Progam文件中加入这一行代码,就可以把指定程序集中所有实现了IRequest<>和INotification的消息类自动生成WebAPI接口。

590-20221124161900132-279581328.png

看起来很神奇,其实也不神奇。主要就是两个字:反射。还有泛型。

简单来说,就是在指定程序集中,通过反射查找那些类实现了IRequest<>或者INotification,然后在通过对泛型映射WebAPI方法的反射调用,为每个消息类生成WebApi接口。

Let me show you the code:

using MediatR;
using MediatRApplication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Reflection;
using System.Xml.Linq;

namespace MediatRWebAPI
{
    public static class MediatorWebAPIExtensions
    {
        /// <summary>
        /// 扩展方法,为所有MediatR Contract 消息类创建WebAPI接口
        /// </summary>
        /// <param name="app"></param>
        /// <param name="assemblies">Contract 消息类所在程序集</param>
        /// <returns></returns>
        public static IEndpointRouteBuilder MapMediatorWebAPIs(this IEndpointRouteBuilder app, params Assembly[] assemblies)
        {
            //为所有实现了IRequest<>的消息类创建WebAPI接口
            Type genericRequestType = typeof(IRequest<>);
            var sendMethodInfo = typeof(MediatorWebAPIExtensions).GetMethod("MapMediatorSendApi", BindingFlags.NonPublic | BindingFlags.Static);
            foreach (var assembly in assemblies)
            {
                //获取该程序集中所有实现了IRequest<>的消息类类型
                var requestTypes = assembly.GetTypes().Where(type => !type.IsInterface && type.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == genericRequestType));
                foreach (var requestType in requestTypes)
                {
                    //获取IRequest<>中尖括号中的泛型参数类型。
                    var responseType = requestType.GetInterfaces().First(t => t.IsGenericType && t.GetGenericTypeDefinition() == genericRequestType).GetGenericArguments().First();
                    //反射调用泛型映射WebApi方法
                    var genericMethod = sendMethodInfo.MakeGenericMethod(requestType, responseType);
                    genericMethod.Invoke(null, new object[] { app, requestType.Name });
                }

            }
            //为所有实现了INotification的消息类创建WebAAPI接口
            Type genericNotificationType = typeof(INotification);
            var publishMethodInfo = typeof(MediatorWebAPIExtensions).GetMethod("MapMediatorPublishApi", BindingFlags.NonPublic | BindingFlags.Static);
            foreach (var assembly in assemblies)
            {
                //获取该程序集中所有实现了INotification的消息类类型
                var requestTypes = assembly.GetTypes().Where(type => !type.IsInterface && genericNotificationType.IsAssignableFrom(type));
                foreach (var requestType in requestTypes)
                {
                    //反射调用泛型映射WebApi方法
                    var genericMethod = publishMethodInfo.MakeGenericMethod(requestType);
                    genericMethod.Invoke(null, new object[] { app, requestType.Name });
                }

            }

            return app;
        }


        /// <summary>
        /// 为实现了IRequest<>的消息类为映射为WebAPI接口,根据消息类名称生成对应的CRUDD Http Method。
        /// </summary>
        /// <typeparam name="TRequest"></typeparam>
        /// <typeparam name="TResponse"></typeparam>
        /// <param name="app"></param>
        /// <param name="requestTypeName"></param>
        internal static void MapMediatorSendApi<TRequest, TResponse>(IEndpointRouteBuilder app, string requestTypeName) where TRequest : IRequest<TResponse>
        {
            if (requestTypeName.StartsWith("Create")) //Http Post
            {
                var uri = new Uri(requestTypeName.Replace("Create", ""), UriKind.Relative);
                app.MapPost(uri.ToString(), async ([FromServices] IMediator mediator, [FromBody] TRequest request) =>
                {
                    TResponse response = await mediator.Send(request);
                    return Results.Created(uri, response);
                }).WithName(requestTypeName).WithOpenApi();
            }
            else if (requestTypeName.StartsWith("Read")) //Http Get
            {
                var uri = new Uri(requestTypeName.Replace("Read", ""), UriKind.Relative);
                app.MapGet(uri.ToString(), async ([FromServices] IMediator mediator, [FromBody] TRequest request) =>
                {
                    TResponse response = await mediator.Send(request);
                    return Results.Ok(response);
                }).WithName(requestTypeName).WithOpenApi();
            }
            else if (requestTypeName.StartsWith("Update")) //Http Put
            {
                var uri = new Uri(requestTypeName.Replace("Update", ""), UriKind.Relative);
                app.MapPut(uri.ToString(), async ([FromServices] IMediator mediator, [FromBody] TRequest request) =>
                {
                    TResponse response = await mediator.Send(request);
                    return Results.Ok(response);
                }).WithName(requestTypeName).WithOpenApi();
            }
            else if (requestTypeName.StartsWith("Delete")) //Http Delete
            {
                var uri = new Uri(requestTypeName.Replace("Delete", ""), UriKind.Relative);
                app.MapDelete(uri.ToString(), async ([FromServices] IMediator mediator, [FromBody] TRequest request) =>
                {
                    TResponse response = await mediator.Send(request);
                    return Results.NoContent();
                }).WithName(requestTypeName).WithOpenApi();
            }
            else  //如不匹配则生成MediatR Send WebAPI接口
            {
                app.MapPost("/mediatr/send/" + requestTypeName, async ([FromServices] IMediator mediator, [FromBody] TRequest request) =>
                {
                    TResponse response = await mediator.Send(request);
                    return Results.Ok(response);
                }).WithName(requestTypeName).WithOpenApi();
            }
        }

        /// <summary>
        /// 为实现了INotification的消息类映射WebAPI接口。
        /// </summary>
        /// <typeparam name="TNotification"></typeparam>
        /// <param name="app"></param>
        /// <param name="requestTypeName"></param>
        internal static void MapMediatorPublishApi<TNotification>(IEndpointRouteBuilder app, string requestTypeName) where TNotification : INotification
        {
            app.MapPost("/mediatr/publish/" + requestTypeName, async ([FromServices] IMediator mediator, [FromBody] TNotification notification) =>
            {
                await mediator.Publish(notification);
                return Results.Ok();
            }).WithName(requestTypeName).WithOpenApi();
        }
    }
}

如上就是实现这个功能的所有代码,为了让大家看明白,我加了很多注释。如果哪位小伙伴还不明白就在下面留言。这些代码最难的地方就是对于泛型接口的处理。

我的示例项目如下,代码已经上传到了GitHub :iamxiaozhuang/MediatRWebAPI (github.com)  大家随便用。

590-20221124165747654-1758720240.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK