services.AddMvc(options => { var readerFactory = services.BuildServiceProvider().GetRequiredService<IHttpRequestStreamReaderFactory>(); options.ModelBinderProviders.Insert(0, new MixModelBinderProvider(options.InputFormatters, readerFactory)); }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
ApiController特性本质
.NET Core每个版本的迭代更新都带给我们最佳体验,直到.NET Core 2.0版本我们知道MVC和Web Api将控制器合并也就是共同继承自Controller,但是呢,毕竟如果仅仅只是做Api开发所以完全用不到MVC中Razor视图引擎,在.NET Core 2.1版本出现了ApiController特性, 同时出现了新的约定,也就是我们控制器基类可以不再是Controller而是ControllerBase,这是一个更加轻量的控制器基类,它不支持Razor视图引擎,ControllerBase控制器和ApiController特性结合使用,完全演变成干净的Api控制器,所以到这里至少我们了解到了.NET Core中的Controller和ControllerBase区别所在,Controller包含Razor视图引擎,而要是如果我们仅仅只是做接口开发,则只需使用ControllerBase控制器结合ApiController特性即可。那么问题来了,ApiController特性的出现到底为我们带来了什么呢?说的更加具体一点则是,它为我们解决了什么问题呢?有的人说.NET Core中模型绑定系统或者ApiController特性的出现显得很复杂,其实不然,只是我们不了解背后它所解决的应用场景,一旦用了之后,发现各种问题呈现出来了,还是基础没有夯实,接下来我们一起来看看。在讲解模型绑定系统时,我们了解到对于参数的验证我们需要通过代码ModelState.IsValid来判断,比如如下代码:
public class Employee { public int Id { get; set; } [Required] public string Address { get; set; } } [Route("[Controller]")] public class ModelBindController : Controller { [HttpPost] public IActionResult Post([FromBody]Employee employee) { if (!ModelState.IsValid) { return BadRequest(ModelState); } return Ok(); } }
当我们请求参数中未包含Address属性时,此时通过上述模型验证未通过响应400。当控制器通过ApiController修饰时,此时内置会自动进行验证,也就是我们不必要在控制器方法中一遍遍写ModelState.IsValid方法,那么问题来了,内置到底是如何进行自动验证的呢?首先会在.NET Core应用程序初始化时,注入如下接口以及具体实现。
services.TryAddEnumerable( ServiceDescriptor.Transient<IApplicationModelProvider, ApiBehaviorApplicationModelProvider>());
那么针对ApiBehaviorApplicationModelProvider这个类到底做了什么呢?在此类构造函数中添加了6个约定,其他四个不是我们研究的重点,有兴趣的童鞋可以私下去研究,我们看看最重要的两个类:InvalidModelStateFilterConvention和InferParameterBindingInfoConvention,然后在此类中有如下方法:
public void OnProvidersExecuting(ApplicationModelProviderContext context) { foreach (var controller in context.Result.Controllers) { if (!IsApiController(controller)) { continue; } foreach (var action in controller.Actions) { // Ensure ApiController is set up correctly EnsureActionIsAttributeRouted(action); foreach (var convention in ActionModelConventions) { convention.Apply(action); } } } }
至于方法OnProviderExecuting方法在何时被调用我们无需太多关心,这不是我们研究的重点,我们看到此方法中的具体就是做了判断我们是否在控制器上通过ApiController进行了修饰,如果是,则遍历我们默认添加的6个约定,好了接下来我们首先来看InvalidModelStateFilterConvention约定,最终我们会看到此类中添加了ModelStateInvalidFilterFactory,然后针对此类的实例化ModelStateInvalidFilter类,然后在此类中我们看到实现了IAactionFilter接口,如下:
public void OnActionExecuting(ActionExecutingContext context) { if (context.Result == null && !context.ModelState.IsValid) { _logger.ModelStateInvalidFilterExecuting(); context.Result = _apiBehaviorOptions.InvalidModelStateResponseFactory(context); } }
到这里想必我们明白了在控制器上通过ApiController修饰解决了第一个问题:在添加MVC框架时,会为我们注入一个ModelStateInvalidFilter,并在OnActionExecuting方法期间运行,也就是执行控制器方法时运行,当然也是在进行模型绑定之后自动进行ModelState验证是否有效,未通过则立即响应400。到这里是不是就这样完事了呢,显然不是,为何,我们在控制器上通过ApiController来进行修饰,如下代码: