我们知道在mvc5和之前的版本,两个框架的生命周期是不一样的,在新版mvc6中,mvc controller/web api controller已经合二为一了,本章我们主要讲解controller和action的定义与使用,以及在mvc框架中,如何根据路由查询相应的controller和action。
controller&action的定义和使用
在新版mvc6框架中,依然提供了一个controller基类,在这里除了依然提供了url、routedata、httpcontext、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程序集下的所有源码。
发表评论