大家好,我是失業在家,正在找工作的博主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接口。
看起來很神奇,其實也不神奇。主要就是兩個字:反射。還有泛型。
簡單來說,就是在指定程序集中,通過反射查找那些類實現了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)? 大家隨便用。