做石膏选图形的网站,微信公众号的微网站开发,外国网站打开慢怎么办,中小网站建设都有哪些方案从何说起这来自于我把项目迁移到Asp.Net Core的过程中碰到一个问题。在一个web程序中同时包含了MVC和WebAPI#xff0c;现在需要给WebAPI部分单独添加一个接口验证过滤器IActionFilter#xff0c;常规做法一般是写好过滤器后给需要的控制器挂上这个标签#xff0c;高级点的做… 从何说起这来自于我把项目迁移到Asp.Net Core的过程中碰到一个问题。在一个web程序中同时包含了MVC和WebAPI现在需要给WebAPI部分单独添加一个接口验证过滤器IActionFilter常规做法一般是写好过滤器后给需要的控制器挂上这个标签高级点的做法是注册一个全局过滤器这样可以避免每次手动添加同时代码也更好管理。注册全局过滤器的方式为 services.AddMvc(options {options.Filters.Add(typeof(AccessControlFilter));});但这样做会带来一个问题那就是MVC部分控制器也会受影响虽然可以在过滤器中进行一些判断来区分哪些是MVC Controller哪些是API Controller但是平白无故给MVC增加这么一个没用的Filter反正我是不能忍所以寻找有没有更好的办法来实现这个功能。于是ModelConvention可以翻译为模型约定闪亮登场。先认识下ApplicationModel看一下官方文档是怎么描述应用程序模型ApplicationModel的ASP.NET Core MVC defines an application model representing the components of an MVC app. You can read and manipulate this model to modify how MVC elements behave. By default, MVC follows certain conventions to determine which classes are considered to be controllers, which methods on those classes are actions, and how parameters and routing behave. You can customize this behavior to suit your apps needs by creating your own conventions and applying them globally or as attributes.简单一点说ApplicationModel描述了MVC应用中的各种对象和行为这些内容包含Application、Controller、Action、Parameter、Router、Page、Property、Filter等等而Asp.Net Core框架本身内置一套规则Convention用来处理这些模型同时也提供了接口给我们自定义约定来扩展模型以实现更符合需要的应用。和应用程序模型有关的类都定义在命名空间Microsoft.AspNetCore.Mvc.ApplicationModels中这些模型通过IApplicationModelProvider 构建出来Asp.Net Core框架提供的默认Provider是DefaultApplicationModelProvider。我们可以编辑这些模型从而更改它的表现行为这就要借助它的ModelConvention来实现。ModelConventionModelConvention定义了操作模型的入口又或者说是一种契约通过它我们可以对模型进行修改常用的Convention包括IApplicationModelConventionIControllerModelConventionIActionModelConventionIParameterModelConventionIPageRouteModelConvention这些接口提供了一个共同的方法Apply方法参数是各自的应用程序模型以IControllerModelConvention为例看一下它的定义namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{//// 摘要:// Allows customization of the Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerModel.//// 言论// To use this interface, create an System.Attribute class which implements the// interface and place it on a controller class. Microsoft.AspNetCore.Mvc.ApplicationModels.IControllerModelConvention// customizations run after Microsoft.AspNetCore.Mvc.ApplicationModels.IApplicationModelConvention// customizations and before Microsoft.AspNetCore.Mvc.ApplicationModels.IActionModelConvention// customizations.public interface IControllerModelConvention{//// 摘要:// Called to apply the convention to the Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerModel.//// 参数:// controller:// The Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerModel.void Apply(ControllerModel controller);}
}从接口摘要可以看到这个接口允许自定义ControllerModel对象而如何自定义内容正是通过Apply方法来实现这个方法提供了当前ControllerModel对象的实例我们可以在它身上获取到的东西实在太多了看看它包含些什么有了这些我们可以做很多很灵活的操作例如通过设置ControllerName字段强制更改控制器的名称让程序中写死的控制器名失效也可以通过Filters字段动态更新它的过滤器集合通过RouteValues来更改路由规则等等。说到这里很多人会觉得这玩意儿和自定义过滤器看起来差不多最开始我也这么认为但经过实际代码调试我发现它的生命周期要比过滤器早的多或者说根本无法比较这个家伙只需要在应用启动时执行一次并不用随着每次请求而执行。也就是说它的执行时间比激活控制器还要早那时候根本没有过滤器什么事儿它的调用是发生在app.UseEndpoints()。回到最开始的需求。基于上面的介绍我们可以自定义如下的约定 public class ApiControllerAuthorizeConvention : IControllerModelConvention{public void Apply(ControllerModel controller){if (controller.Filters.Any(x x is ApiControllerAttribute) !controller.Filters.Any(x x is AccessControlFilter)){controller.Filters.Add(new AccessControlAttribute());}}}上面的主要思路就是通过判断控制器本身的过滤器集合是否包含ApiControllerAttribute来识别是否API Controller如果是API Controller并且没有标记过AccessControlAttribute的话就新建一个实例加入进去。那么如何把这个约定注册到应用中呢在Microsoft.AspNetCore.Mvc.MvcOptions中提供了Conventions属性 //// 摘要:// Gets a list of Microsoft.AspNetCore.Mvc.ApplicationModels.IApplicationModelConvention// instances that will be applied to the Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModel// when discovering actions.public IListIApplicationModelConvention Conventions { get; }通过操作它就能把自定义约定注入进去 services.AddMvc(options {options.Conventions.Add(new ApiControllerAuthorizeConvention());})细心的人会发现Conventions是一个IApplicationModelConvention类型的集合而我们自定义的Convention是一个IControllerModelConvention正常来说应该会报错才对原因是Asp.Net Core的DI框架帮我们提供了一系列扩展方法来简化Convention的添加不用自己再去转换通过代码调试发现应用启动时遍历了系统中的所有控制器去执行Apply操作那么通过IApplicationModelConvention一样也能实现这个功能因为它里面包含了控制器集合 public class ApiControllerAuthorizeConvention : IApplicationModelConvention{public void Apply(ApplicationModel application){foreach (var controller in application.Controllers){if (controller.Filters.Any(x x is ApiControllerAttribute) !controller.Filters.Any(x x is AccessControlFilter)){controller.Filters.Add(new AccessControlFilter());}}}}再改进一下实际开发中我的AccessControlFilter需要通过构造函数注入业务接口类似于这样 public class AccessControlFilter : IActionFilter{private IUserService _userService;public AccessControlFilter(IUserService service){_userService service;}public void OnActionExecuting(ActionExecutingContext context){//模拟一下业务操作//var user_userService.GetById(996);//.......}public void OnActionExecuted(ActionExecutedContext context){}}如何优雅的在Convention中使用DI自动注入呢Asp.Net Core MVC框架提供的ServiceFilter可以解决这个问题ServiceFilter本身是一个过滤器它的不同之处在于能够通过构造函数接收一个Type类型的参数我们可以在这里把真正要用的过滤器传进去于是上面的过滤器注册过程演变为 controller.Filters.Add(new ServiceFilterAttribute(typeof(AccessControlFilter)));当然了要从DI中获取这个filter实例必须要把它注入到DI容器中 services.AddScopedAccessControlFilter();至此大功告成继续愉快的CRUD。突然想起来我上篇文章提到的扩展DI属性注入功能估计也能通过这个玩意实现eeeeeee...有空了试一下。总结总体来说我通过曲线救国的方式实现了全局过滤器隔离虽然去遍历目标控制器再手动添加Filter的方式没有那种一行代码就能实现的方式优雅但我大体来说还算满意是目前能想到的最好办法。我估摸着options.Filters.Add(xxx)也是在框架某个时候一个个把xxx丢给各自主人的瞎猜的说错不负责