当前位置: 移动技术网 > IT编程>开发语言>.net > 解读ASP.NET 5 & MVC6系列教程(10):Controller与Action

解读ASP.NET 5 & MVC6系列教程(10):Controller与Action

2017年12月12日  | 移动技术网IT编程  | 我要评论

唐僧志,怎样锻炼肺活量,旱冰鞋视频

我们知道在mvc5和之前的版本,两个框架的生命周期是不一样的,在新版mvc6中,mvc controller/web api controller已经合二为一了,本章我们主要讲解controller和action的定义与使用,以及在mvc框架中,如何根据路由查询相应的controller和action。

controller&action的定义和使用

在新版mvc6框架中,依然提供了一个controller基类,在这里除了依然提供了urlroutedatahttpcontextrequestresponse以外,还提供了一个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程序集下的所有源码。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网