前言
极客时间上的《.NET Core开发实战》是一门非常好的课程,作者肖伟宇在第31课(https://time.geekbang.org/course/detail/100044601-201165)介绍了定义API的最佳实践。
大意如下:
Controller这一层负责与前端用户的交互,它主要的责任就是定义输入和输出,不应该处理业务。
因此使用中介者模式,将业务逻辑和Controller进行隔离。
示例API的实现代码如下:
[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{IMediator _mediator;public OrderController(IMediator mediator){_mediator = mediator;}[HttpPost]public async Task<long> CreateOrder([FromBody]CreateOrderCommand cmd){return await _mediator.Send(cmd, HttpContext.RequestAborted);}[HttpGet]public async Task<List<string>> QueryOrder([FromQuery]MyOrderQuery myOrderQuery){return await _mediator.Send(myOrderQuery);}
}public class CreateOrderCommand : IRequest<long>
{public CreateOrderCommand(int itemCount){ItemCount = itemCount;}public long ItemCount { get; private set; }
}public class MyOrderQuery : IRequest<List<string>>
{public string UserName { get; set; }
}
问题
按照上述的最佳实践去编写Controller,这就意味着,如果有100个命令和查询,就要创建100个同样格式的方法。
这是一项重复并且枯燥的工作,而且还必须保证方法返回值与IRequest<T>
的类型一致,很容易出错。
有不有不用写这些代码的简单方式?
Source Generators
现在,可以祭出我们的大杀器——Source Generators。
上次我们已经使用它实现了AutoMapper,它的主要特点是自动生成源代码并编译到最终输出中。
这次,我们用它来自动生成API代码。
实现原理如下:
遍历所有继承
IRequest<T>
的类,并用它们名称的第2个单词(例如Order)分组分组Key作为Controller的名称
在Controller分组中遍历分组下的类,生成对应命令和查询方法
具体实现代码请到https://github.com/feiyun0112/CodesForMy_IO下载。
使用示例
下载课件代码(https://gitee.com/geektime-geekbang/NET-Core),在GeekTime.Ordering.API项目中引用ApiControllerGenerator
项目,无需修改任何代码,直接编译,可以看到已经按照最佳实践的规范生成了代码(Controller名称故意加了New以示区别):
运行后Swagger显示正常,成功!
结论
现在,我们只需定义命令和查询,即可实现定义API的最佳实践,是不是很酷!
如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我!