如何在 ASP.NET Core 中写出更干净的 Controller

开发 前端
你可以遵循一些最佳实践来写出更干净的 Controller,一般我们称这种方法写出来的 Controller 为瘦Controller,瘦 Controller 的好处在于拥有更少的代码,更加单一的职责,也便于阅读和维护,而且随着时间的推移也容易做 Controller 的多版本。

 [[374901]]

本文转载自微信公众号「 码农读书」,作者 码农读书。转载本文请联系 码农读书公众号。

你可以遵循一些最佳实践来写出更干净的 Controller,一般我们称这种方法写出来的 Controller 为瘦Controller,瘦 Controller 的好处在于拥有更少的代码,更加单一的职责,也便于阅读和维护,而且随着时间的推移也容易做 Controller 的多版本。

这篇文章我们一起讨论那些让 Controler 变胖变臃肿的一些坏味道,并且一起探索让 Controller 变瘦的手段,虽然我的一些在 Controller 上的最佳实践可能不是专业的,但我每一步都提供相关源代码来进行优化,接下来的章节中,我们会讨论什么是 胖Controller,什么是 坏味道,什么是 瘦Controller,它能带给我们什么福利?并且如何让 Controller 变瘦,变简单,利测试,易维护。

从 Controller 中移除数据层代码

当在写 Controller 的时候,你应该遵守 单一职责,也就意味着你的 Controller 只需做一件事情,换句话说,只有一个因素或者唯一一个因素能让你修改 Controller 中的代码,如果有点懵的话,考虑下面的代码片段,它将 数据访问代码 糅进了 Controller 。

  1. public class AuthorController : Controller 
  2.     private AuthorContext dataContext = new AuthorContext(); 
  3.     public ActionResult Index(int authorId) 
  4.     { 
  5.         var authors = dataContext.Authors 
  6.             .OrderByDescending(x=>x.JoiningDate) 
  7.             .Where(x=>x.AuthorId == authorId) 
  8.             .ToList(); 
  9.         return View(authors); 
  10.     } 
  11.     //Other action methods 

请注意上面的代码在 Action 中使用了 dataContext 从数据库读取数据,这就违反了单一职责原则,并直接导致了 Controller 的臃肿。

假如后续你需要修改 数据访问层 代码,可能基于更好的性能或者你能想到的原因,这时候只能被迫在 Controller 中修改,举个例子吧:假如你想把上面的 EF 改成 Dapper 去访问底层的 Database,更好的做法应该是单独拎出来一个 repository 类来操控 数据访问 相关的代码,下面是更新后的 AuthorController。

  1. public class AuthorController : Controller 
  2.     private AuthorRepository authorRepository = new AuthorRepository(); 
  3.     public ActionResult Index(int authorId) 
  4.     { 
  5.         var authors = authorRepository.GetAuthor(authorId); 
  6.         return View(authors); 
  7.     } 
  8.     //Other action methods 

现在 AuthorController 看起来是不是精简多了,上面的代码是不是就是最佳实践呢?不完全是,为什么这么说呢?上面这种写法导致 Controller 变成了 数据访问组件,取出数据后必然少不了一些业务逻辑处理,这就让 Controller 违反了 单一职责,对吧,更通用的做法应该是将 数据访问逻辑 封装在一个 service 层,下面是优化之后的 AuthorController 类。

  1. public class AuthorController : Controller 
  2.     private AuthorService authorService = new AuthorService(); 
  3.     public ActionResult Index(int authorId) 
  4.     { 
  5.         var authors = authorService.GetAuthor(authorId); 
  6.         return View(authors); 
  7.     } 
  8.     //Other action methods 

再看一下 AuthorService 类,可以看到它利用了 AuthorRepository 去做 CURD 操作。

  1. public class AuthorService 
  2.     private AuthorRepository authorRepository = new AuthorRepository(); 
  3.     public Author GetAuthor (int authorId) 
  4.     { 
  5.         return authorRepository.GetAuthor(authorId); 
  6.     } 
  7.     //Other methods 

避免写大量代码做对象之间映射

在 DDD 开发中,经常会存在 DTO 和 Domain 对象,在数据 Input 和 Output 的过程中会存在这两个对象之间的 mapping,按照普通的写法大概就是这样的。

  1. public IActionResult GetAuthor(int authorId) 
  2.     var author = authorService.GetAuthor(authorId); 
  3.     var authorDTO = new AuthorDTO(); 
  4.     authorDTO.AuthorId = author.AuthorId; 
  5.     authorDTO.FirstName = author.FirstName; 
  6.     authorDTO.LastName = author.LastName; 
  7.     authorDTO.JoiningDate = author.JoiningDate; 
  8.     //Other code 
  9.    ...... 

可以看到,这种一一映射的写法让 Controller 即时膨胀,同时也让 Controller 增加了额外的功能,那如何把这种 模板式 代码规避掉呢?可以使用专业的 对象映射框架 AutoMapper 去解决,下面的代码展示了如何做 AutoMapper 的配置。

  1. public class AutoMapping 
  2.     public static void Initialize() 
  3.     { 
  4.         Mapper.Initialize(cfg => 
  5.         { 
  6.             cfg.CreateMap<Author, AuthorDTO>(); 
  7.             //Other code             
  8.         }); 
  9.     } 

接下来可以在 Global.asax 中调用 Initialize() 初始化,如下代码所示:

  1. protected void Application_Start() 
  2.     AutoMapping.Initialize();          

最后,可以将 mapping 逻辑放在 service 层中,请注意下面的代码是如何使用 AutoMapper 实现两个不兼容对象之间的映射。

  1. public class AuthorService 
  2.     private AuthorRepository authorRepository = new AuthorRepository(); 
  3.     public AuthorDTO GetAuthor (int authorId) 
  4.     { 
  5.         var author = authorRepository.GetAuthor(authorId); 
  6.         return Mapper.Map<AuthorDTO>(author); 
  7.     } 
  8.     //Other methods 

避免在 Controller 中写业务逻辑

尽量避免在 Controller 中写 业务逻辑 或者 验证逻辑, Controller 中应该仅仅是接收一个请求,然后被下一个 action 执行,别无其它,回到刚才的问题,这两种逻辑该怎么处理呢?

  • 业务逻辑

这些逻辑可以封装 XXXService 类中,比如之前创建的 AuthorService。

  • 验证逻辑

这些逻辑可以用 AOP 的操作手法,比如将其塞入到 Request Pipeline 中处理。

使用依赖注入而不是硬组合

推荐在 Controller 中使用依赖注入的方式来实现对象之间的管理,依赖注入是 控制反转 的一个子集,它通过外部注入对象之间的依赖从而解决内部对象之间的依赖,很拗口是吧!

一旦你用了依赖注入方式,就不需要关心对象是怎么实例化的,怎么初始化的,下面的代码展示了如何在 AuthorController 下的构造函数中实现 IAuthorService 对象的注入。

  1. public class AuthorController : Controller 
  2.     private IAuthorService authorService = new AuthorService(); 
  3.     public AuthorController(IAuthorService authorService) 
  4.     { 
  5.        this.authorService = authorService; 
  6.     } 
  7.    // Action methods 

使用 action filer 消除 Controller 中的重复代码

可以利用 action filter 在 Request pipeline 这个管道的某些点上安插一些你的自定义代码,举个例子,可以使用 ActionFilter 在 Action 的执行前后安插一些自定义代码,而不是将这些业务逻辑放到 Controller 中,让 Controller 不必要的膨胀,下面的代码展示了如何去实现。

  1. [ValidateModelState] 
  2. [HttpPost] 
  3. public ActionResult Create(AuthorRequest request) 
  4.     AuthorService authorService = new AuthorService(); 
  5.     authorService.Save(request); 
  6.     return RedirectToAction("Home"); 

总的来说,如果一个 Controller 被赋予了几个职责,那么只要是其中任何一个职责的原因,你都必须对 Controller 进行修改,总的来说,一定要坚守 单一原则。

译文链接:https://www.infoworld.com/article/3404472/how-to-write-efficient-controllers-in-aspnet-core.html

 

责任编辑:武晓燕 来源: 码农读书
相关推荐

2021-03-17 09:45:31

LazyCacheWindows

2021-02-02 16:19:08

Serilog日志框架

2021-02-06 21:40:13

SignalR通讯TypeScript

2021-11-01 14:52:38

ElasticSear索引SQL

2021-01-13 07:33:41

API数据安全

2021-01-15 05:38:28

ASPHttp端口

2021-01-07 07:39:07

工具接口 Swagger

2021-03-10 09:40:43

LamarASP容器

2021-01-28 22:39:35

LoggerMessa开源框架

2021-02-28 20:56:37

NCache缓存框架

2021-02-03 13:35:25

ASPweb程序

2021-01-31 22:56:50

FromServiceASP

2021-03-03 22:37:16

MediatR中介者模式

2021-03-08 07:32:05

Actionweb框架

2021-02-07 17:29:04

监视文件接口

2021-03-18 07:33:54

PDF DinkToPdfC++

2009-08-05 11:00:46

获得RowIndexASP.NET

2021-01-04 05:44:54

框架日志

2021-02-19 06:54:33

配置系统ASP.NET Cor

2022-08-01 08:00:00

开发工具跟踪侦听器
点赞
收藏

51CTO技术栈公众号