asp.net mvc-Controllerl篇 ControllerDescriptor
程序员文章站
2024-03-06 15:43:14
现在我们首先来看看actioninvoker属性的定义吧: 复制代码 代码如下: public iactioninvoker actioninvoker { get { i...
现在我们首先来看看actioninvoker属性的定义吧:
public iactioninvoker actioninvoker {
get {
if (_actioninvoker == null) {
_actioninvoker = createactioninvoker();
}
return _actioninvoker;
}
set {
_actioninvoker = value;
}
}
protected virtual iactioninvoker createactioninvoker() {
return new controlleractioninvoker();
}
和tempdataprovider属性定义一样,大家一定要习惯这些代码啊。
而controlleractioninvoker的定义也很简单,但是这个类却不简单啊。
让我们来看看你invokeaction的定义吧:
public virtual bool invokeaction(controllercontext controllercontext, string actionname) {
if (controllercontext == null) {
throw new argumentnullexception("controllercontext");
}
if (string.isnullorempty(actionname)) {
throw new argumentexception(mvcresources.common_nullorempty, "actionname");
}
controllerdescriptor controllerdescriptor = getcontrollerdescriptor(controllercontext);
actiondescriptor actiondescriptor = findaction(controllercontext, controllerdescriptor, actionname);
if (actiondescriptor != null) {
filterinfo filterinfo = getfilters(controllercontext, actiondescriptor);
try {
authorizationcontext authcontext = invokeauthorizationfilters(controllercontext, filterinfo.authorizationfilters, actiondescriptor);
if (authcontext.result != null) {
// the auth filter signaled that we should let it short-circuit the request
invokeactionresult(controllercontext, authcontext.result);
}
else {
if (controllercontext.controller.validaterequest) {
validaterequest(controllercontext);
}
idictionary<string, object> parameters = getparametervalues(controllercontext, actiondescriptor);
actionexecutedcontext postactioncontext = invokeactionmethodwithfilters(controllercontext, filterinfo.actionfilters, actiondescriptor, parameters);
invokeactionresultwithfilters(controllercontext, filterinfo.resultfilters, postactioncontext.result);
}
}
catch (threadabortexception) {
// this type of exception occurs as a result of response.redirect(), but we special-case so that
// the filters don't see this as an error.
throw;
}
catch (exception ex) {
// something blew up, so execute the exception filters
exceptioncontext exceptioncontext = invokeexceptionfilters(controllercontext, filterinfo.exceptionfilters, ex);
if (!exceptioncontext.exceptionhandled) {
throw;
}
invokeactionresult(controllercontext, exceptioncontext.result);
}
return true;
}
// notify controller that no method matched
return false;
}
这个方法里面的内容不可能一次讲完的,我们看看 controllerdescriptor controllerdescriptor = getcontrollerdescriptor(controllercontext);
很明显controllerdescriptor是controller实例的一个包装类。
protected virtual controllerdescriptor getcontrollerdescriptor(controllercontext controllercontext) {
type controllertype = controllercontext.controller.gettype();
controllerdescriptor controllerdescriptor = descriptorcache.getdescriptor(controllertype, () => new reflectedcontrollerdescriptor(controllertype));
return controllerdescriptor;
}
从这个方法中,我们可以知道实际返回的是一个reflectedcontrollerdescriptor实例,它是controllerdescriptor的子类,descriptorcache.getdescriptor(...)好像是从缓存中获取的啊,让我们证实一下吧,先来看看controllerdescriptorcache的getdescriptor方法:
internal sealed class controllerdescriptorcache : readerwritercache<type, controllerdescriptor> {
public controllerdescriptor getdescriptor(type controllertype, func<controllerdescriptor> creator) {
return fetchorcreateitem(controllertype, creator);
}
}
fetchorcreateitem方法很简单,从缓存中获取controllerdescriptor ,如果没有就创建并加入缓存然后在返回,缓存实现方式其实就是一个字典dictionary<tkey, tvalue>。
现在看看reflectedcontrollerdescriptor的够着函数是否有什么特别之处:
_controllertype = controllertype;
_selector = new actionmethodselector(_controllertype);
怎么又有actionmethodselector这个东东啊,其构造函数如下
public actionmethodselector(type controllertype) {
controllertype = controllertype;
populatelookuptables();
}
private void populatelookuptables() {
methodinfo[] allmethods = controllertype.getmethods(bindingflags.invokemethod | bindingflags.instance | bindingflags.public);
methodinfo[] actionmethods = array.findall(allmethods, isvalidactionmethod);
aliasedmethods = array.findall(actionmethods, ismethoddecoratedwithaliasingattribute);
nonaliasedmethods = actionmethods.except(aliasedmethods).tolookup(method => method.name, stringcomparer.ordinalignorecase);
}
这个方法很简单,找出controllertype的所有实例、共有方法,然后在过滤调不是action的,最后吧这些action方法分成两部分,一部分有别名,一部分没有别名。
现在我们已经得到了controllerdescriptor实例,下面应该来看看actiondescriptor actiondescriptor = findaction(controllercontext, controllerdescriptor, actionname);这句代码了;同样我们可以确认actiondescriptor实际上一个action的包装类。
protected virtual actiondescriptor findaction(controllercontext controllercontext, controllerdescriptor controllerdescriptor, string actionname)这个方法实际上就是调用
controllerdescriptor类findaction方法,让我们看看你reflectedcontrollerdescriptor的findaction方法,该方法很简单,组要就2句代码:
methodinfo matched = _selector.findactionmethod(controllercontext, actionname);
return new reflectedactiondescriptor(matched, actionname, this);
_selector.findactionmethod(controllercontext, actionname); 这句就是找到我们需要action对应的methodinfo。
public methodinfo findactionmethod(controllercontext controllercontext, string actionname) {
list<methodinfo> methodsmatchingname = getmatchingaliasedmethods(controllercontext, actionname);
methodsmatchingname.addrange(nonaliasedmethods[actionname]);
list<methodinfo> finalmethods = runselectionfilters(controllercontext, methodsmatchingname);
switch (finalmethods.count) {
case 0:
return null;
case 1:
return finalmethods[0];
default:
throw createambiguousmatchexception(finalmethods, actionname);
}
}
循环每个methodinfo,查找它们的自定义的actionmethodselectorattribute特性,如果有只返回验证通过的特性。看到reflectedattributecache.getactionmethodselectorattributes(methodinfo)这样的代码感觉由于缓存有关,
private static readonlycollection<tattribute> getattributes<tmemberinfo, tattribute>(concurrentdictionary<tmemberinfo, readonlycollection<tattribute>> lookup, tmemberinfo memberinfo)
where tattribute : attribute
where tmemberinfo : memberinfo {
return lookup.getoradd(memberinfo, mi => new readonlycollection<tattribute>((tattribute[])memberinfo.getcustomattributes(typeof(tattribute), inherit: true)));
}
reflectedattributecache这个类有几个缓存字典:
concurrentdictionary<methodinfo, readonlycollection<actionmethodselectorattribute>>
concurrentdictionary<methodinfo, readonlycollection<actionnameselectorattribute>>
concurrentdictionary<methodinfo, readonlycollection<filterattribute>>
concurrentdictionary<type, readonlycollection<filterattribute>>
默认实现actionmethodselectorattribute类主要有以下几个
acceptverbsattribute
httpdeleteattribute
httpgetattribute
httppostattribute
httpputattribute
nonactionattribute
acceptverbsattribute
剩下的就是直接实例一个reflectedactiondescriptor对象了,这个也没什么特殊,只是里面有一个验证方法
if (validatemethod) {
string failedmessage = verifyactionmethodiscallable(methodinfo);
if (failedmessage != null) {
throw new argumentexception(failedmessage, "methodinfo");
}
}
用来验证该方法是否可以执行,以下几种情况经不会通过,(1)方法是静态方法(2)方法的实例类型不是controllerbase(3)是否包含泛型参数如 public actionresult index<t>()是非法的,但 public actionresult index(list<string> aa)是合法(4)参数中不能含有ref和out。
这篇文章说的很散,我们需要注意一点微软在mvc里面缓存做的很好了,在前面个将获取controllertyper时它是有缓存的,一次读取当前程序集所有的controllertype,在这里提到了一个descriptorcache 缓存每次调用的controllertype->reflectedcontrollerdescriptor,而reflectedcontrollerdescriptor实例会一次去读该controller的所有action方法;这里还有一个reflectedattributecache,缓存每次调用methodinfo的所有特性(actionmethodselectorattribute、actionnameselectorattribute、filterattribute),当然filterattribute特性还可以在类上面。
复制代码 代码如下:
public iactioninvoker actioninvoker {
get {
if (_actioninvoker == null) {
_actioninvoker = createactioninvoker();
}
return _actioninvoker;
}
set {
_actioninvoker = value;
}
}
protected virtual iactioninvoker createactioninvoker() {
return new controlleractioninvoker();
}
和tempdataprovider属性定义一样,大家一定要习惯这些代码啊。
而controlleractioninvoker的定义也很简单,但是这个类却不简单啊。
让我们来看看你invokeaction的定义吧:
复制代码 代码如下:
public virtual bool invokeaction(controllercontext controllercontext, string actionname) {
if (controllercontext == null) {
throw new argumentnullexception("controllercontext");
}
if (string.isnullorempty(actionname)) {
throw new argumentexception(mvcresources.common_nullorempty, "actionname");
}
controllerdescriptor controllerdescriptor = getcontrollerdescriptor(controllercontext);
actiondescriptor actiondescriptor = findaction(controllercontext, controllerdescriptor, actionname);
if (actiondescriptor != null) {
filterinfo filterinfo = getfilters(controllercontext, actiondescriptor);
try {
authorizationcontext authcontext = invokeauthorizationfilters(controllercontext, filterinfo.authorizationfilters, actiondescriptor);
if (authcontext.result != null) {
// the auth filter signaled that we should let it short-circuit the request
invokeactionresult(controllercontext, authcontext.result);
}
else {
if (controllercontext.controller.validaterequest) {
validaterequest(controllercontext);
}
idictionary<string, object> parameters = getparametervalues(controllercontext, actiondescriptor);
actionexecutedcontext postactioncontext = invokeactionmethodwithfilters(controllercontext, filterinfo.actionfilters, actiondescriptor, parameters);
invokeactionresultwithfilters(controllercontext, filterinfo.resultfilters, postactioncontext.result);
}
}
catch (threadabortexception) {
// this type of exception occurs as a result of response.redirect(), but we special-case so that
// the filters don't see this as an error.
throw;
}
catch (exception ex) {
// something blew up, so execute the exception filters
exceptioncontext exceptioncontext = invokeexceptionfilters(controllercontext, filterinfo.exceptionfilters, ex);
if (!exceptioncontext.exceptionhandled) {
throw;
}
invokeactionresult(controllercontext, exceptioncontext.result);
}
return true;
}
// notify controller that no method matched
return false;
}
这个方法里面的内容不可能一次讲完的,我们看看 controllerdescriptor controllerdescriptor = getcontrollerdescriptor(controllercontext);
很明显controllerdescriptor是controller实例的一个包装类。
复制代码 代码如下:
protected virtual controllerdescriptor getcontrollerdescriptor(controllercontext controllercontext) {
type controllertype = controllercontext.controller.gettype();
controllerdescriptor controllerdescriptor = descriptorcache.getdescriptor(controllertype, () => new reflectedcontrollerdescriptor(controllertype));
return controllerdescriptor;
}
从这个方法中,我们可以知道实际返回的是一个reflectedcontrollerdescriptor实例,它是controllerdescriptor的子类,descriptorcache.getdescriptor(...)好像是从缓存中获取的啊,让我们证实一下吧,先来看看controllerdescriptorcache的getdescriptor方法:
复制代码 代码如下:
internal sealed class controllerdescriptorcache : readerwritercache<type, controllerdescriptor> {
public controllerdescriptor getdescriptor(type controllertype, func<controllerdescriptor> creator) {
return fetchorcreateitem(controllertype, creator);
}
}
fetchorcreateitem方法很简单,从缓存中获取controllerdescriptor ,如果没有就创建并加入缓存然后在返回,缓存实现方式其实就是一个字典dictionary<tkey, tvalue>。
现在看看reflectedcontrollerdescriptor的够着函数是否有什么特别之处:
_controllertype = controllertype;
_selector = new actionmethodselector(_controllertype);
怎么又有actionmethodselector这个东东啊,其构造函数如下
复制代码 代码如下:
public actionmethodselector(type controllertype) {
controllertype = controllertype;
populatelookuptables();
}
private void populatelookuptables() {
methodinfo[] allmethods = controllertype.getmethods(bindingflags.invokemethod | bindingflags.instance | bindingflags.public);
methodinfo[] actionmethods = array.findall(allmethods, isvalidactionmethod);
aliasedmethods = array.findall(actionmethods, ismethoddecoratedwithaliasingattribute);
nonaliasedmethods = actionmethods.except(aliasedmethods).tolookup(method => method.name, stringcomparer.ordinalignorecase);
}
这个方法很简单,找出controllertype的所有实例、共有方法,然后在过滤调不是action的,最后吧这些action方法分成两部分,一部分有别名,一部分没有别名。
现在我们已经得到了controllerdescriptor实例,下面应该来看看actiondescriptor actiondescriptor = findaction(controllercontext, controllerdescriptor, actionname);这句代码了;同样我们可以确认actiondescriptor实际上一个action的包装类。
protected virtual actiondescriptor findaction(controllercontext controllercontext, controllerdescriptor controllerdescriptor, string actionname)这个方法实际上就是调用
controllerdescriptor类findaction方法,让我们看看你reflectedcontrollerdescriptor的findaction方法,该方法很简单,组要就2句代码:
复制代码 代码如下:
methodinfo matched = _selector.findactionmethod(controllercontext, actionname);
return new reflectedactiondescriptor(matched, actionname, this);
_selector.findactionmethod(controllercontext, actionname); 这句就是找到我们需要action对应的methodinfo。
复制代码 代码如下:
public methodinfo findactionmethod(controllercontext controllercontext, string actionname) {
list<methodinfo> methodsmatchingname = getmatchingaliasedmethods(controllercontext, actionname);
methodsmatchingname.addrange(nonaliasedmethods[actionname]);
list<methodinfo> finalmethods = runselectionfilters(controllercontext, methodsmatchingname);
switch (finalmethods.count) {
case 0:
return null;
case 1:
return finalmethods[0];
default:
throw createambiguousmatchexception(finalmethods, actionname);
}
}
循环每个methodinfo,查找它们的自定义的actionmethodselectorattribute特性,如果有只返回验证通过的特性。看到reflectedattributecache.getactionmethodselectorattributes(methodinfo)这样的代码感觉由于缓存有关,
复制代码 代码如下:
private static readonlycollection<tattribute> getattributes<tmemberinfo, tattribute>(concurrentdictionary<tmemberinfo, readonlycollection<tattribute>> lookup, tmemberinfo memberinfo)
where tattribute : attribute
where tmemberinfo : memberinfo {
return lookup.getoradd(memberinfo, mi => new readonlycollection<tattribute>((tattribute[])memberinfo.getcustomattributes(typeof(tattribute), inherit: true)));
}
reflectedattributecache这个类有几个缓存字典:
concurrentdictionary<methodinfo, readonlycollection<actionmethodselectorattribute>>
concurrentdictionary<methodinfo, readonlycollection<actionnameselectorattribute>>
concurrentdictionary<methodinfo, readonlycollection<filterattribute>>
concurrentdictionary<type, readonlycollection<filterattribute>>
默认实现actionmethodselectorattribute类主要有以下几个
acceptverbsattribute
httpdeleteattribute
httpgetattribute
httppostattribute
httpputattribute
nonactionattribute
acceptverbsattribute
剩下的就是直接实例一个reflectedactiondescriptor对象了,这个也没什么特殊,只是里面有一个验证方法
复制代码 代码如下:
if (validatemethod) {
string failedmessage = verifyactionmethodiscallable(methodinfo);
if (failedmessage != null) {
throw new argumentexception(failedmessage, "methodinfo");
}
}
用来验证该方法是否可以执行,以下几种情况经不会通过,(1)方法是静态方法(2)方法的实例类型不是controllerbase(3)是否包含泛型参数如 public actionresult index<t>()是非法的,但 public actionresult index(list<string> aa)是合法(4)参数中不能含有ref和out。
这篇文章说的很散,我们需要注意一点微软在mvc里面缓存做的很好了,在前面个将获取controllertyper时它是有缓存的,一次读取当前程序集所有的controllertype,在这里提到了一个descriptorcache 缓存每次调用的controllertype->reflectedcontrollerdescriptor,而reflectedcontrollerdescriptor实例会一次去读该controller的所有action方法;这里还有一个reflectedattributecache,缓存每次调用methodinfo的所有特性(actionmethodselectorattribute、actionnameselectorattribute、filterattribute),当然filterattribute特性还可以在类上面。