第 3 章 ASP.NET Core 核心特性
3.3 依赖注入
通常情况下,应用程序由多个组件构成,而组件与组件之间往往存在依赖关系
当我们需要获取数据时,通常的做法是实例化依赖的类,然后调用类里面的方法,但是这种依赖方式会增加调用方和被调用方之间的耦合,也会增加应用程序维护成本及灵活性,同时增加了单元测试的难度
为了解决这一问题,需要用到依赖倒置原则,高层不直接依赖低层,两者均依赖抽象
代码语言:javascript复制public class Book
{
}
public interface IDataService
{
List<Book> GetAllBooks();
}
public class DataService : IDataService
{
public List<Book> GetAllBooks()
{
return new List<Book>();
}
}
public class DisplayDataService
{
private readonly IDataService _dataService;
public DisplayDataService(IDataService dataService)
{
_dataService = dataService;
}
public void ShowData()
{
var data = _dataService.GetAllBooks();
}
}
接下来,只需要在实例化 DisplayDataService 的时候,在构造函数传入一个 IDataService 接口的具体实现即可
代码语言:javascript复制IDataService dataService = new DataService();
DisplayDataService displayDataService = new DisplayDataService(dataService);
除了构造函数注入之外,还有属性注入和方法注入
当应用程序中有多处要用到依赖注入时,就需要一个专门的类来负责管理创建所需要的类并创建所有它可能要用到的依赖,这个类就是依赖注入容器,也可以称为控制反转容器,IOC 容器
在 ASP.NET Core 中,所有被放入依赖注入容器的类型或组件被称为服务
容器中的服务有两种类型:第一种是框架服务,它们是 ASP.NET Core 框架的组成部分;另一种是应用服务,所有由用户放到容器中的服务都属于这一类
在 ASP.NET Core 内置的依赖注入容器中,服务的生命周期有如下3种类型:
- Singleton:容器会创建并共享服务的单例,且一直会存在于应用程序的整个生命周期内
- Transient:每次服务被请求时,总会创建新实例
- Scoped:在每一次请求时会创建服务的新实例,并在这个请求内一直共享这个实例
3.4 MVC
MVC 是模型、视图、控制器的缩写,它是 Web 应用程序中一种常见的架构模式,最主要的优点是实现了关注点分离
在 ASP.NET Core MVC 框架中,除了 Controller、Model 和 Action 外,它还包括路由、模型绑定、模型验证和过滤器等功能
路由的主要功能是根据预先配置的路由信息对客户端传来的请求进行路由映射,映射完成后,再将请求传给对应的路由处理器处理
对于 ASP.NET Core MVC,定义路由的方法有以下两种:
- 基于约定的路由
- 特性路由
基于约定的路由需要在 Startup 类中指明,具体来说,应该在配置 MVC 中间件时来设置路由约定
代码语言:javascript复制app.UseMvc(routes =>
{
routes.MapRoute("default",template: "{controller}/{action}");
});
以下约定为 controller 和 action 设置了默认值,参数 id 后面有一个问号,说明这个参数时可选的,在 URL 中有无此项都可以,注意,一个 URL 模板中只能有一个可选参数,并且只能放在最后
代码语言:javascript复制app.UseMvc(routes =>
{
routes.MapRoute("default",template: "{controller=Home}/{action=Index}/{id?}");
});
指定参数时,也可以添加约束
代码语言:javascript复制app.UseMvc(routes =>
{
routes.MapRoute("default",template: "{controller=Home}/{action=Index}/{id:int}");
});
特性路由只需要在 Controller 类或 Action 方法上添加 [Route] 特性即可
代码语言:javascript复制[Route("Home")]
public class HomeController : Controller
{
[Route("Index")]
public IActionResult Index()
{
return View();
}
}
当 Controller 需要依赖其他服务时,通常的做法是使用构造函数注入所需要的服务,当程序运行时,ASP.NET Core 会在创建 Controller 时自动从其依赖注入的容器中获取所有依赖的服务,需要注意的是,所注入的服务必须存在于容器中,否则会发生异常
Action 的返回结果有以下4类:
- StatusCode
- ObjectResult
- 重定向结果
- 内容结果
模型绑定:将 HTTP 请求中的数据映射到 Action 中参数的过程
ASP.NET Core MVC 模型绑定特性:
- [FromHeader]
- [FromQuery]
- [FromServices]
- [FromRoute]
- [FromForm]
- [FromBody]
还有两个特性用于指明参数是否必须使用绑定:
- BindRequiredAttribute
- BindNeverAttribute
模型验证:指数据被使用之前的验证过程,它发生在模型绑定之后
数据注解通常用于验证,只要为类的属性添加需要的数据注解验证特性即可:
- [Required]
- [MinLength(10)]
- [Url]
- [Range(1,5)]
在 Controller 内的 Action 中,要检查一个对象是否满足指定的条件,只要调用 ModelState.IsVaild 属性,其中 ModelState 是 ControllerBase 类的属性
ASP.NET Core MVC 提供两种创建自定义验证的方法:
- 创建新特性,并使它继承自 ValidationAttribute 类
- 使待验证的 Model 实现 IValidatableObject 接口
过滤器:与中间件很相似,在 ASP.NET Core MVC 中,它们能够在某些功能的前后执行,由此形成一个管道
ASP.NET Core MVC 提供了以下5种类型的过滤器:
- Authorization
- Resource
- Action
- Exception
- Result
当要创建过滤器时,应该实现 IXXXFilter 或 IAsyncXXXFilter,前者同步,后者异步,实现一个即可
在 startup 种注册过滤器会使它影响到应用中的每个 Action,如果要仅为一个或少数几个 Action 添加过滤器,就得使用特性,ASP.NET Core 为每一种类型的过滤器都定义了相应的特性
如果以特性的方式使用包含依赖项的过滤器时,就会出错,因为在自定义特性的构造函数中定义的接口类型的参数并不是有效的特性参数,此时需要使用 [ServiceFilter] 特性或者 [TypeFilter] 特性,并设置它们的 Type 属性为自定义过滤器类型
[ServiceFilter] 特性与 [TypeFilter] 特性的区别是前者会从容器中获取过滤器实例,而后者不会,它使用 ObjectFactory 对指定的过滤器类型进行实例化,如果使用前者,需要在 Startup 类的 ConfigureServices 方法中将该过滤器添加到容器中