大家好,我是失业在家,正在找工作的博主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)  大家随便用。

根据MediatR的Contract Messages自动生成Minimal WebApi接口的更多相关文章

  1. iBatis——自动生成DAO层接口提供操作函数(详解)

    iBatis——自动生成DAO层接口提供操作函数(详解) 在使用iBatis进行持久层管理时,发现在使用DAO层的updateByPrimaryKey.updateByPrimaryKeySelect ...

  2. Groovy元编程应用之自动生成订单搜索接口测试用例集

    背景 在 "Groovy元编程简明教程" 一文中,简明地介绍了 Groovy 元编程的特性. 那么,元编程可以应用哪些场合呢?元编程通常可以用来自动生成一些相似的模板代码. 在 & ...

  3. 【Golang】基于录制,自动生成go test接口自动化用例

    背景 之前写过一篇博客,介绍怎么用Python通过解析抓包数据,完成自动化用例的编写.最近这段时间在使用go test,所以就在想能不能也使用代码来生成自动化用例,快速提升测试用例覆盖率.说干就干. ...

  4. VS2017+WIN10自动生成类、接口的说明(修改类模板的方法)

    微软发布VS2017的时候,我第一时间离线一份专业版,安装到了自己的电脑上,开始体验,但是问题来了,在开发中建立类和接口的时候,说 明注释总要自己写一次,烦!~~于是还是像以前一样改IDE默认的类和接 ...

  5. 使用swagger实现在线api文档自动生成 在线测试api接口

    使用vs nuget包管理工具搜索Swashbuckle 然后安装便可 注释依赖于vs生成的xml注释文件

  6. 自动生成web api接口文档

    然后打开web程序,访问ip:port/Help. 为什么可以直接输入Help就能访问呢,因为这个插件本身已经配置了路径,如下. public class HelpPageAreaRegistrati ...

  7. spring和mybatis集成,自动生成model、mapper,增加mybatis分页功能

    软件简介 Spring是一个流行的控制反转(IoC)和面向切面(AOP)的容器框架,在java webapp开发中使用广泛.http://projects.spring.io/spring-frame ...

  8. idea中自动生成实体类

    找到生成实体的路径,找到Database数据表 找到指定的路径即可自动生成entity实体 在创建好的实体类内如此修改 之后的步骤都在脑子里  写给自己看的东西 哪里不会就记录哪里 test类(以前都 ...

  9. swagger 自动生成接口测试用例

    ---整体更新一波--- 1.实际工作中,因为要动手输入的地方比较多,自动生成的异常接口用例感觉用处不大,就先去掉了,只保留了正常的: 2.接口有改动的,如果开发人员没有及时告知或没有详细告知,会增加 ...

  10. springboot和mybatis集成,自动生成model、mapper,增加mybatis分页功能

    整体思路和http://www.cnblogs.com/mahuan2/p/5859921.html相同. 主要讲maven的pom.xml和一些配置变化,详细说明. 软件简介 Spring是一个流行 ...

随机推荐

  1. 《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(19)-Fiddler精选插件扩展安装,将你的Fiddler武装到牙齿

    1.简介 Fiddler本身的功能其实也已经很强大了,但是Fiddler官方还有很多其他扩展插件功能,可以更好地辅助Fiddler去帮助用户去开发.测试和管理项目上的任务.Fiddler已有的功能已经 ...

  2. 《Win10——常用快捷键》

    Win10--常用快捷键       Ctrl+C:复制 Ctrl+V:粘贴 Ctrl+A:全选 Ctrl+X:剪切 Ctrl+D:删除 Ctrl+Z:撤销 Ctrl+Y:反撤销 Ctrl+Shift ...

  3. Python数据分析教程(一):Numpy

    原文链接:https://blog.onefly.top/posts/13140.html 数据的纬度 一维数据:列表和集合类型 二维数据:列表类型 多维数据:列表类型 高维数据:字典类型或数据表示格 ...

  4. avue常用场景记录

    接手的一个项目使用的是avue这个傻瓜式的专门给后端人员用的框架,文档不够友好,使用起来各种蛋疼(咱专业前端基本上不使用).为此,专门记录一下.当前avue版本2.8.12,如果要切换avue的版本, ...

  5. kibana配置文件kibana.yml参数详解

    server.port: 默认值: 5601 Kibana 由后端服务器提供服务,该配置指定使用的端口号. server.host: 默认值: "localhost" 指定后端服务 ...

  6. Elasticsearch:用户安全设置

    Elastic Stack的组件是不安全的,因为它没有内置的固有安全性. 这意味着任何人都可以访问它. 在生产环境中运行Elastic Stack时,这会带来安全风险. 为了防止生产中未经授权的访问, ...

  7. 项目的依赖包(node_modules)删除

    快速删除依赖包一共分为三部 1.打开命令行(管理员身份),执行 npm i -g npkill 2.cd 进入到想删除的项目中,执行 npkill 3.执行完成会进入到npkill页面,等待搜索完成, ...

  8. MySQL学习(3)---MySQL常用命令

    ps:此随笔基于mysql 5.7.*版本. 准备 net start mysql 启动MySQL服务 net stop mysql 关闭MySQL服务 mysql [-h<IP地址>] ...

  9. 密码学奇妙之旅、02 混合加密系统、AES、RSA标准、Golang代码

    CTR 计数器模式 计数器模式CTR是分组密码模式中的一种.通过将逐次累加的计数器进行加密来生成密钥流的流密码.每次加密时会生成一个不同的值来作为计数器的初始值. 可以事先进行加密.解密的准备. 加密 ...

  10. PHP全栈开发(三):CentOS 7 中 PHP 环境搭建及检测

    简单回顾一下我们在(一).(二)中所做的工作. 首先我们在(一)中设置了CentOS 7的网络. 其实这些工作在CentOS 6中都是很容易的,因为有鸟哥的Linux私房菜这样好的指导. 但是这些操作 ...