Controller与Action
Controller与Action
我们知道在MVC5和之前的版本,两个框架的生命周期是不一样的,在新版MVC6中,MVC Controller/Web API Controller已经合二为一了,本章我们主要讲解Controller和Action的定义与使用,以及在MVC框架中,如何根据路由查询相应的Controller和Action。
Controller&Action的定义和使用
在新版MVC6框架中,依然提供了一个Controller基类,在这里除了依然提供了Url、RouteData、HttpContent、Request、Response以外,还提供了一个IServiceProvider类型的Resovler属性,该属于是依赖注入的容器,用于获取当前请求作用域内指定类型的实例对象。
其遵守如下规则:
- 继承于
Microsoft.AspNet.Mvc.Controller的类肯定都是控制器,不管有没有Controller后缀。 - 不继承
Microsoft.AspNet.Mvc.Controller的自定义XXXController要作为MVC Controller的话,,则必须要引用Microsoft.AspNet.Mvc相关的程序集。 - 如果不想让满足上述条件的Controller类作为Controller,需要在该类上加上
NonControllerAttribute特性。 - 同理,如果不想让某个Controller中的方法作为Action,则需要在该方法上加上
NonActionAttribute特性。
另外还有如下几个特性需要注意:
| 特性 | 描述 |
|---|---|
| ActionNameAttribute | 定义Action的名称(可以和Action方法名不同) |
| AcceptVerbsAttribute | 定义支持的Http Method名称,支持单个或多个Method。 |
| ActivateAttribute | 依赖注入的标记,可以放在具有set权限的属性或字段上。 |
| ResponseCacheAttribute | 针对某个Controller或Action设置客户端缓存。 |
| RequireHttpsAttribute | 限制必须是Https请求。 |
| RemoteAttribute | 标记为Ajax请求,服务器端不验证form表单的验证。 |
| NonControllerAttribute | 标记该类不是Controller。 |
| NonActionAttribute | 标记该方法不是Action。 |
Controller的查找机制
由上述章节,我们知道MVC6不仅支持正常的Controller(继承于Controller基类的子类),也支持POCO的Controller,本节我们就来研究一下Controller的查找原理机制。
首先,要判断一个类是否是Controller必须先确定有多少个程序集里定义了这样的类。Microsoft.AspNet.Mvc命名空间下的IAssemblyProvider接口就是覆盖查找所有可能定义Controller的程序集,该接口的默认实现是DefaultAssemblyProvider类,在该类中,设置的必要条件是,定义了MVC的Controller必须要引用了如下程序集中的一个或多个程序集,列表如下:
Microsoft.AspNet.Mvc
Microsoft.AspNet.Mvc.Core
Microsoft.AspNet.Mvc.ModelBinding
Microsoft.AspNet.Mvc.Razor
Microsoft.AspNet.Mvc.Razor.Host
Microsoft.AspNet.Mvc.TagHelpers
Microsoft.AspNet.Mvc.Xml
Microsoft.AspNet.PageExecutionInstrumentation.Interfaces
也就是说,如果你定义了一个引用了Microsoft.AspNet.Mvc的DLL类库的话,其里面的POCO Controller都会被认为是MVC的Controller。换句话说,如果你定义的POCO Controller类没有引用上述程序集中的任意一个程序集,那这些Controller类不会被认为是MVC的Controller。
程序集的查找
目前有两种方式可以自定义Controller的查找机制,第一种是继承IAssemblyProvider实现CandidateAssemblies方法(或重载DefaultAssemblyProvider),来定义自己的逻辑。接口定义如下:
public interface IAssemblyProvider
{
IEnumerable<Assembly> CandidateAssemblies { get; }
}
另外一种方式,可能相对来说更简单一些,那就是使用IServicesCollection上定义的扩展方法来定义要查找的程序集:
services.AddMvc().WithControllersAsServices(new[]
{
typeof(MyController).Assembly,
typeof(ExternalPocoController).Assembly
});
使用上述代码后,系统将会把DefaultAssemblyProvider切换成FixedSetAssemblyProvider来实现上述判断机制,即:在固定范围内的程序集里进行查找。
程序集的筛选
确定了程序集以后,另外一个问题就来了,如何判断一个程序集是否引用了上述MVC必要条件中所列的程序集呢?答案是,Microsoft.Framework.Runtime中的ILibraryManager接口实例的GetReferencingLibraries方法,可以查找有多少个程序集引用了上述列表中的其中一个程序集。例如,可以根据Microsoft.AspNet.Mvc程序集,来查找有多少个程序集引用了该程序集,示例如下:
var col = this.Resolver.GetRequiredService<ILibraryManager>();
var data = col.GetReferencingLibraries("Microsoft.AspNet.Mvc");
该功能在DefaultAssemblyProvider默认实现类中的使用代码如下:
protected virtual IEnumerable<ILibraryInformation> GetCandidateLibraries()
{
if (ReferenceAssemblies == null)
{
return Enumerable.Empty<ILibraryInformation>();
}
// GetReferencingLibraries returns the transitive closure of referencing assemblies
// for a given assembly.
return ReferenceAssemblies.SelectMany(_libraryManager.GetReferencingLibraries)
.Distinct()
.Where(IsCandidateLibrary);
}
Controller的判断
确定了符合必要条件的程序集之后,就可以遍历该程序集内所有的类型,并接着判断该类型是否是Controller了。在新版的Controller判断上,实现该功能的是一个IControllerTypeProvider接口,该接口提供了一个ControllerTypes只读属性用于获取所有定义的Controller,接口定义如下:
public interface IControllerTypeProvider
{
IEnumerable<TypeInfo> ControllerTypes { get; }
}
DefaultControllerTypeProvider是该接口的默认实现,在查询符合条件的Controller的时候,该默认实现类定义了一个IsController方法,用于判断一个类型是否是Controller,具体逻辑如下:
protected internal virtual bool IsController([NotNull] TypeInfo typeInfo,
[NotNull] ISet<Assembly> candidateAssemblies)
{
if (!typeInfo.IsClass) // 该类型必须是一个类
{
return false;
}
if (typeInfo.IsAbstract) // 该类必须不是抽象类
{
return false;
}
// We only consider public top-level classes as controllers. IsPublic returns false for nested
// classes, regardless of visibility modifiers
if (!typeInfo.IsPublic) // 该类必须是一个Public类(并且不嵌套),嵌套类不能作为Controller
{
return false;
}
if (typeInfo.ContainsGenericParameters) // 该类不能是泛型类
{
return false;
}
if (!typeInfo.Name.EndsWith(ControllerTypeName, StringComparison.OrdinalIgnoreCase) &&
!DerivesFromController(typeInfo, candidateAssemblies)) // 该类以Controller结尾,或继承于Controller基类,或其父类也是Controller。
{
return false;
}
if (typeInfo.IsDefined(typeof(NonControllerAttribute))) // 该类不能设置NonControllerAttribute特性
{
return false;
}
return true;
}
你也可以自己实现IControllerTypeProvider接口来定义自己的Controller判断逻辑,不过和固定某些程序集类型,MVC在IServicesCollection上也提供了一个扩展方法,用于限制一些Controller特定类型,示例如下:
services.AddMvc().WithControllersAsServices(new[]
{
typeof(MyController),
typeof(ExternalPocoController)
});
使用上述代码后,系统将会把DefaultControllerTypeProvider切换成FixedSetControllerTypeProvider来实现上述判断机制,即:限制某些特定的类作为Controller,其它类型都不能作为Controller。
Action的查找机制
Action的选择则是通过IActionSelector接口的默认实现类DefaultActionSelector来实现的,在实现的SelectAsync方法中,通过上下文和路由数据选择最匹配的Action,示意代码如下:
public Task<ActionDescriptor> SelectAsync([NotNull] RouteContext context)
{
// ...
}
还有一个地方会判断一个方法是否是Action,那就是IActionModelBuilder接口,该接口的默认实现为DefaultActionModelBuilder类,实现方法如下:
public IEnumerable<ActionModel> BuildActionModels([NotNull] TypeInfo typeInfo,
[NotNull] MethodInfo methodInfo)
{
if (!IsAction(typeInfo, methodInfo))
{
return Enumerable.Empty<ActionModel>();
}
// ....省略其它代码
}
该实现方法,通过一个内部的IsAction方法来判断该方法是否是一个真正的Action方法,具体代码如下:
protected virtual bool IsAction([NotNull] TypeInfo typeInfo, [NotNull] MethodInfo methodInfo)
{
// The SpecialName bit is set to flag members that are treated in a special way by some compilers
// (such as property accessors and operator overloading methods).
if (methodInfo.IsSpecialName) // 不能是特殊名称(如重载的操作符或属性访问器)
{
return false;
}
if (methodInfo.IsDefined(typeof(NonActionAttribute))) // 不能声明NonActionAttribute特性
{
return false;
}
// Overriden methods from Object class, e.g. Equals(Object), GetHashCode(), etc., are not valid.
if (methodInfo.GetBaseDefinition().DeclaringType == typeof(object)) //不能是重载的方法,比如Equals和GetHashCode
{
return false;
}
// Dispose method implemented from IDisposable is not valid
if (IsIDisposableMethod(methodInfo, typeInfo)) // 不能是Dispose方法
{
return false;
}
if (methodInfo.IsStatic) // 不能是静态方法
{
return false;
}
if (methodInfo.IsAbstract) // 不能是抽象方法
{
return false;
}
if (methodInfo.IsConstructor) // 不能是构造函数
{
return false;
}
if (methodInfo.IsGenericMethod) // 不能是泛型方法
{
return false;
}
return
methodInfo.IsPublic; // 必须是Public方法
}
以上内容就是关于Controller和Action查找相关的重要代码,详细原理步骤,请参考Microsoft.AspNet.Mvc.Core程序集下的所有源码。
同步与推荐
本文已同步至目录索引:解读ASP.NET 5 & MVC6系列
Controller与Action的更多相关文章
- 实现MVC自定义过滤器,自定义Area过滤器,自定义Controller,Action甚至是ViewData过滤器
MVC开发中几种以AOP方式实现的Filters是非常好用的,默认情况下,我们通过App_Start中的FilterConfig来实现的过滤器注册是全局的,也就是整个应用程序都会使用的,针对单独的Fi ...
- 解读ASP.NET 5 & MVC6系列(10):Controller与Action
我们知道在MVC5和之前的版本,两个框架的生命周期是不一样的,在新版MVC6中,MVC Controller/Web API Controller已经合二为一了,本章我们主要讲解Controller和 ...
- [ASP.NET MVC 小牛之路]09 - Controller 和 Action (1)
我们知道,在 MVC 中每个请求都会提交到 Controller 进行处理.Controller 是和请求密切相关的,它包含了对请求的逻辑处理,能对 Model 进行操作并选择 View 呈现给用户, ...
- [ASP.NET MVC 小牛之路]10 - Controller 和 Action (2)
继上一篇文章之后,本文将介绍 Controller 和 Action 的一些较高级特性,包括 Controller Factory.Action Invoker 和异步 Controller 等内容. ...
- 在ASP.NET MVC 中获取当前URL、controller、action
一.URL的获取很简单,ASP.NET通用: [1]获取 完整url (协议名+域名+虚拟目录名+文件名+参数) string url=Request.Url.ToString(); [2]获取 虚拟 ...
- Asp.Net_Mvc_获取当前Url、Controller、Action
一.URL的获取很简单,ASP.NET通用: [1]获取 完整url (协议名+域名+虚拟目录名+文件名+参数) string url=Request.Url.ToString(); [2]获取 虚拟 ...
- 004. Asp.Net Routing与MVC 之二: 请求如何激活Controller和Action
上篇讲到 请求到达 MvcRouteHandler ,并且透过 IRouteHandler.GetHttpHandler 获取到了真正的处理程序 MvcHandler 这次我们看看,MvcHandle ...
- [译] ASP.NET MVC 6 attribute routing – the [controller] and [action] tokens
原文:http://www.strathweb.com/2015/01/asp-net-mvc-6-attribute-routing-controller-action-tokens/ 当在Web ...
- Yii里获取当前controller和action的id
Yii里获取当前controller和action的id 在控制器里$name = $this->getId(); // controller$name = $action->id; ...
- C# mvc中为Controller或Action添加定制特性实现登录验证
在本文开始前,先简单讲两个知识点: 1.每个action执行前都会先执行OnActionExecuting方法: 2.FCL提供了多种方式来检测特性的存在,比如IsDefined.GetCustomA ...
随机推荐
- javascript从定义到执行 js引擎 闭包
javascript从定义到执行,JS引擎在实现层做了很多初始化工作,因此在学习JS引擎工作机制之前,我们需要引入几个相关的概念:执行环境 栈.全局对象.执行环境.变量对象.活动对象.作用域和作用域链 ...
- Java采用HttpClient对于Web登录
http://e.neusoft.edu.cn/nav_login 模拟浏览器登录该网站上方.登录server基于验证码.refer和cookie保护,此代码html档. import java.io ...
- Spring HTTPInvoker原理猜想(HTTP+序列化)
没有查看源码,仅作参考 实现步骤: 一,客户端 (1),远程调用信息封装为远程调用对象 (2),序列化写入到远程调用HTTP请求中 (3),向服务器发送 (4),服务器端返回的HTTP响应结果 (5) ...
- 于win7使用虚拟磁盘隐藏文件
于win7使用虚拟磁盘隐藏文件,我只是win7在验证.其他型号未知. 一.创建虚拟磁盘 1.右键点击"计算机"-----"管理" ------"磁盘管 ...
- [LeetCode257]Binary Tree Paths
题目: Given a binary tree, return all root-to-leaf paths. For example, given the following binary tree ...
- UVA11294-Wedding(2-SAT)
option=com_onlinejudge&Itemid=8&page=show_problem&problem=2269">题目链接 题意:有n对夫妻參加一 ...
- Effective C++:规定27:尽量少做动作的过渡
(一个)C风格遗留转换: (T)expression T(expression) (二)C++提供四种新式转型: (1)const_cast<T>(expression):去除表达式的常量 ...
- java 遍历树节点 同时保留所有的从根到叶节点的路径
直接在代码.稍后细说 数据结构定义: /** * */ package Servlet; import java.util.ArrayList; import java.util.List; /** ...
- 答读者问(5):关于数学程序猿的作用、r \\ u0026研发工作的实践要求和问题,如求职的影响
最近,有通过微博很多读者.微信沟通,我.我觉得我们学习.对于技术,我们很热情.我也学到了很多东西. 我提取了几个大家比較关心的问题予以答复.请有相同疑问的朋友參考一下. ,欢迎关注.) 版权声明:本文 ...
- js在方法Ajax请求数据来推断,验证无效(OnClientClick="return Method();"),或者直接运行的代码隐藏
function CheckAdd() { var flag = true; $.ajax({ cache: false, async: false, url: "/ajaxpage/get ...