31 | APIController:定义API的最佳实践

首先看一个传统意义上三层架构定义的 Controller

[HttpPost]
public Task<long> CreateOrder([FromBody]CreateOrderVeiwModel viewModel)
{
var model = viewModel.ToModel();
return await orderService.CreateOrder(model);
} class OrderService : IOrderService
{
public long CreateOrder(CreateOrderModel model)
{
var address = new Address("wen san lu", "hangzhou", "310000");
var order = new Order("xiaohong1999", "xiaohong", 25, address); _orderRepository.Add(order);
await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
return order.Id;
}
}

可以看到这里的 Controller 负责模型转换,还负责服务调用,服务里面实际上就是领域模型的操作部分

随着业务逻辑的越来越复杂,Controller 会越来越膨胀,在 DDD 领域驱动设计的理念下,我们更倾向于把应用程序的每一层明确区分,然后层与层之间的界限应该是明确的,在实现上面应该也是隔离的

Controller 这一层负责与前端用户的交互,它主要的责任就是定义输入和输出,实现身份认证,授权功能,它不应该处理领域模型,处理仓储,所以不建议以上的写法,不建议在 Controller 里面写模型转换和服务调用

namespace GeekTime.API.Controllers
{
[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([FromBody]MyOrderQuery myOrderQuery)
{
return await _mediator.Send(myOrderQuery);
}
}
}

这里使用了中间者模式 Mediator,它通过把命令发送出去,然后我们在 Commands 目录下面定义了每一个命令的 handler,这样就可以将业务逻辑的部分和 Controller 处理的部分,输入输出定义的部分进行隔离,我们的 Controller 还需要去定义路由的规则,路由验证的规则

再看一下 Controller 的构造函数,从设计上建议 Controller 所依赖的服务都通过它的构造函数注入进来,之前有讲过,通过容器进行属性注入的方式,但这种方式我们并不推荐使用,当一个 Controller 依赖了很多服务的时候,可以发现有一部分服务是大部分的 Action 都会依赖到的,有一部分服务只是个别 Action 依赖到的,这个时候就可以使用 FromServices,而不需要在构造函数里面注入它,这样有个好处是在编写单元测试的时候,可以在容器里面 Mock 所有的服务

public async Task<long> CreateOrder([FromServices] IEventBus eventBus, [FromBody]CreateOrderCommand cmd)

这里不建议使用属性注入的方式来注入服务,是因为使用属性注入的时候,会把这些属性,比如说 IOrderService,有可能由其他代码 set 我们的 OrderService,造成意外的情况,使我们的代码的维护不可控

public IOrderService orderService { get; set; }

还有一个关键的点是建议尽可能定义异步的 action,尽可能地使用 async 和 await 这样的组合来实现我们的代码,这样对提高我们应用程序的吞吐量是有一定的帮助的

总结一下

APIController 实际上是负责了对前端用户的输入输出的定义,它还负责了身份验证,授权,Url 定义的部分

APIController 不应该负责业务逻辑的承载,应该把这些职责交给我们命令处理程序或者说领域服务来定义

再一个我们也讲解了 APIController 在注入服务时的一些方法,通过构造函数的注入,通过 FromServices 的方式获取服务,不建议的做法时使用属性注入的方式注入

GitHub源码链接:

https://github.com/witskeeper/geektime

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。

.NET Core开发实战(第31课:APIController:定义API的最佳实践)--学习笔记的更多相关文章

  1. 2月送书福利:ASP.NET Core开发实战

    大家都知道我有一个公众号“恰童鞋骚年”,在公众号2020年第一天发布的推文<2020年,请让我重新介绍我自己>中,我曾说到我会在2020年中每个月为所有关注“恰童鞋骚年”公众号的童鞋们送一 ...

  2. [ASP.NET Core开发实战]开篇词

    前言 本系列课程文章主要是学习官方文档,再输出自己学习心得,希望对你有所帮助. 课程大纲 本系列课程主要分为三个部分:基础篇.实战篇和部署篇. 希望通过本系列课程,能让大家初步掌握使用ASP.NET ...

  3. [ASP.NET Core开发实战]基础篇02 依赖注入

    ASP.NET Core的底层机制之一是依赖注入(DI)设计模式,因此要好好掌握依赖注入的用法. 什么是依赖注入 我们看一下下面的例子: public class MyDependency { pub ...

  4. 测试开发实战[提测平台]20-图表G2Plot在项目的实践实录

    微信搜索[大奇测试开],关注这个坚持分享测试开发干货的家伙. G2Plot项目应用 上一篇<提测平台19-Echarts图表在项目的实践>讲解了Echarts的图表应用,此篇来看下开箱即用 ...

  5. .NET Core开发实战(第11课:文件配置提供程序)--学习笔记

    11 | 文件配置提供程序:自由选择配置的格式 文件配置提供程序 Microsoft.Extensions.Configuration.Ini Microsoft.Extensions.Configu ...

  6. 2、SpringBoot接口Http协议开发实战8节课(1-6)

    1.SpringBoot2.xHTTP请求配置讲解 简介:SpringBoot2.xHTTP请求注解讲解和简化注解配置技巧 1.@RestController and @RequestMapping是 ...

  7. [ASP.NET Core开发实战]基础篇03 中间件

    什么是中间件 中间件是一种装配到应用管道,以处理请求和响应的组件.每个中间件: 选择是否将请求传递到管道中的下一个中间件. 可在管道中的下一个中间件前后执行. ASP.NET Core请求管道包含一系 ...

  8. [ASP.NET Core开发实战]基础篇01 Startup

    Startup,顾名思义,就是启动类,用于配置ASP.NET Core应用的服务和请求管道. Startup有两个主要作用: 通过ConfigureServices方法配置应用的服务.服务是一个提供应 ...

  9. 2、SpringBoot接口Http协议开发实战8节课(7-8)

    7.SpringBoot2.x文件上传实战 简介:讲解HTML页面文件上传和后端处理实战 1.讲解springboot文件上传 MultipartFile file,源自SpringMVC 1)静态页 ...

  10. [ASP.NET Core开发实战]基础篇06 配置

    配置,是应用程序很重要的组成部分,常常用于提供信息,像第三方应用登录钥匙.上传格式与大小限制等等. ASP.NET Core提供一系列配置提供程序读取配置文件或配置项信息. ASP.NET Core项 ...

随机推荐

  1. S3C2440移植uboot之启动过程概述

      上节烧写了uboot到开发板,不能运行.这节我们分析uboot重新编译uboot,由最后一条链接命令开始分析uboot 目录 1.分析start.S 2._start会跳转到start_code处 ...

  2. Linux 查看office文件及pdf文件

    1.查看pdf文件 evince PdfFile_name 查看office文件 openoffice.org 文件名 & // 打开或者编辑.doc.odt等文本文档命令 openoffic ...

  3. Python Code_04InputFunction

    代码部分 # coding:utf-8 # author : 写bug的盼盼 # development time : 2021/8/28 6:55 present = input('你想要什么?') ...

  4. ONVIF网络摄像头(IPC)客户端开发—ONVIF介绍

    ​ 1.前言: 网上已经有很多关于ONVIF开发的资料,这里概括介绍一下ONVIF协议以及介绍一下我自己在开发ONVIF网络摄像头的一些流程和经验,做个开发记录和经验总结,以备将来查看,也可供他人参考 ...

  5. [转帖]46岁加入谷歌,51岁发明Go,他的编程原则影响了一大批程序员!

    https://www.zhihu.com/tardis/zm/art/551945410?source_id=1005 今年3月,万众瞩目的Go 1.18版本发布,Go终于开始支持泛型了!该版本不仅 ...

  6. vim工具极简总结

    vim工具总结 背景 很多操作记不住. 想着总结当笔记使用. 备忘 基本总结 vim somefile 打开/新建文件 i/a/insert按键 进入插入模式 insert 连续两次 进入替换模式 e ...

  7. [转帖]oracle 11.2.0.4 rac集群等待事件enq: TM - contention

    近期,一金融客户oracle 11.2.0.4 rac集群delete不当导致等待事件enq: TM - contention严重引起大范围会话堆积,记录的相关分析工作如下. 1.登录集群任意节点,查 ...

  8. [转帖]Shell if 条件判断

      Shell  语言中的if条件 一.if的基本语法: if [ command ];then    符合该条件执行的语句 elif [ command ];then    符合该条件执行的语句 e ...

  9. [转帖]TiDB 环境与系统配置检查

    https://docs-archive.pingcap.com/zh/tidb/v6.0/check-before-deployment 本文介绍部署 TiDB 前的环境检查操作,以下各项操作按优先 ...

  10. kafka学习之五_多个磁盘的性能验证

    kafka学习之五_多个磁盘的性能验证 背景 周末在家学习kafka 上午验证了grafana+kafka_exporter的监控 下午想着验证一把性能相关. kafka学习之三里面,有成套的脚本. ...