对InvokeAction简略分析了解验证失败为什么Action还会继续执行
一、前言
有些同学使用AuthorizationFilter来进行用户是否登录验证,如果未登录就跳到登录页。
很简单的一个场景,但是有些同学会发现虽然验证失败了,但是整个Action还会执行一遍。
于是google啊google啊,然后找到了解决方案,但是不知为啥,下面就对InvokeAction的简略分析,说说到底是为啥。
二、分析InvokeAction执行顺序
1、首先获取Controller和Action描述类
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
2、大家都知道每个Action执行前会先执行一些Filters,所以首先需要获取所有FilterScope为Action的Filters
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
3、Filter的执行是有顺序的,AuthenticationFilter最先执行,接下来就是执行AuthenticationFilter的代码
AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);
这里要注意,AuthenticationContext有个属性Result,如果验证失败一定要设置这个属性的值,否则看看下面的代码就知道了。
if (authenticationContext.Result != null)
{
// An authentication filter signaled that we should short-circuit the request. Let all
// authentication filters contribute to an action result (to combine authentication
// challenges). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
authenticationContext.Result);
InvokeActionResult(controllerContext, challengeContext.Result ?? authenticationContext.Result);
}
else
{
...
}
看到了吧,如果你设置Result的值,此时就会执行你设置的Result,如果不设置就会继续走下去,会走哪去,一直往后看,你就知道了。
插一下,我们先看看 InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)是什么东东。
protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
{
actionResult.ExecuteResult(controllerContext);
}
这个方法看字面意思是执行一个ActionResult,还是看两个简单的例子吧。
(1)ContentResult
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
} HttpResponseBase response = context.HttpContext.Response; if (!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
if (Content != null)
{
response.Write(Content);
}
}
很简单吧,就是把你设置的Content输出到页面中,用的也是response.Write(content)方法,所以在Mvc中完全可以用ContentResult替换response.Write(content).
(2)HttpStatusCodeResult
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
} context.HttpContext.Response.StatusCode = StatusCode;
if (StatusDescription != null)
{
context.HttpContext.Response.StatusDescription = StatusDescription;
}
}
HttpStatusCodeResult 是HttpUnauthorizedResult 的基类,HttpUnauthorizedResult是什么?自己看字面意思也看出来了。
这个Result的ExecuteResult的更简单,只是设置Response.StatusCode和StatusDescription。
4、回到InvokeAction中来,接着InvokeAuthenticationFilters这个方法的执行往下看
if (authenticationContext.Result != null)已经解释了,然后看看else。else中就开始执行AuthorizationFilters了,此处终于知道为什么AuthenticationFilters执行在前了吧。
只有authenticationContext.Result不设置值的时候,才会执行AuthorizationFilters,所以如果你想AuthenticationFilters验证失败之后,就退出Action,一定要设置Result.
其实AuthorizationFilters的执行方式和AuthenticationFilters类似,如果设置了AuthorizationContext的Result属性,就会执行Result,否则就继续往下执行。
AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
if (authorizationContext.Result != null)
{
// An authorization filter signaled that we should short-circuit the request. Let all
// authentication filters contribute to an action result (to combine authentication
// challenges). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
authorizationContext.Result);
InvokeActionResult(controllerContext, challengeContext.Result ?? authorizationContext.Result);
}
else
{
// 如果你没有其他的Filters、没有设置OnActionXXX等等,你的Action内的代码就会在这里被调用执行了。
...
}
看到这里,是不是有些同学会想:“原来如此啊”。
三、总结
由于微软没有提供AuthenticationFilter对应的基类,只提供了一个IAuthenticationFilter,所以我们就用AuthorizeAttribute来进行总结。
一些同学自定义AuthorizationFilters的时候,代码结尾没有调用基类的 OnAuthorization,也没有设置FilterContext的Result属性,只是进行Response.Redirect或者其他认为跳出Action执行的代码。这样其实等于没有进行验证,Action中的代码还是会执行。
在进行AuthorizationFilters的时候,如果想验证失败之后,不执行Action的方法,可以有以下两种方法:
(1)调用基类的 OnAuthorization,我们看看基类 OnAuthorization 都干了啥?
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
... if (AuthorizeCore(filterContext.HttpContext))
{
...
}
else
{
HandleUnauthorizedRequest(filterContext); // 主要看这个方法
}
}
protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
filterContext.Result = new HttpUnauthorizedResult();
}
HttpUnauthorizedResult:这个类有没有看着眼熟,对了,就是前面讲Result中的第二个例子的子类。
(2)给filterContext的Result属性设置一个Result,即使是EmptyResult都可以,如果你想输出点内容到页面中,那就使用ContetResult,前面说过它是通过调用Response.Write()来实现的。
最后附上ControllerActionInvoker:InvokeAction(L230)方法的详细内容:
public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
} Contract.Assert(controllerContext.RouteData != null);
if (String.IsNullOrEmpty(actionName) && !controllerContext.RouteData.HasDirectRouteMatch())
{
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
{
AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor); if (authenticationContext.Result != null)
{
// An authentication filter signaled that we should short-circuit the request. Let all
// authentication filters contribute to an action result (to combine authentication
// challenges). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
authenticationContext.Result);
InvokeActionResult(controllerContext, challengeContext.Result ?? authenticationContext.Result);
}
else
{
AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
if (authorizationContext.Result != null)
{
// An authorization filter signaled that we should short-circuit the request. Let all
// authentication filters contribute to an action result (to combine authentication
// challenges). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
authorizationContext.Result);
InvokeActionResult(controllerContext, challengeContext.Result ?? authorizationContext.Result);
}
else
{
if (controllerContext.Controller.ValidateRequest)
{
ValidateRequest(controllerContext);
} IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters); // The action succeeded. Let all authentication filters contribute to an action result (to
// combine authentication challenges; some authentication filters need to do negotiation
// even on a successful result). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
postActionContext.Result);
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters,
challengeContext.Result ?? 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;
}
对InvokeAction简略分析了解验证失败为什么Action还会继续执行的更多相关文章
- php:微信公众号token验证失败原因、验证码显示不出来的问题
ob_clean(); 问题描述: 用微信官方提供的demo验证token是成功的,但是放到自己网站的框架上进行token验证老是提示"token验证失败",经过检查(用生成日志的 ...
- Token验证失败
Token验证失败 微信 微信公众平台开发 Token校验失败 URL Token原文 http://www.cnblogs.com/txw1958/p/token-verify.html Token ...
- 微信公众号token验证失败的一些总结
这几天准备弄一个微信公众号,在进行服务器配置的时候出现总是出现token验证失败的报错. 实际上,这个问题很好解决.既然微信平台没有给我们很明确的报错提示,那么我们就可以通过跟踪获取到的请求参数进行分 ...
- Token验证失败的解决方法
Token验证失败 微信 微信公众平台开发 Token校验失败 URL Token原文 http://www.cnblogs.com/txw1958/p/token-verify.html Token ...
- oracle导入时提示IMP-00010:不是有效的导出文件,头部验证失败
oracle导入时提示IMP-00010:不是有效的导出文件,头部验证失败: 原因分析:导出的oracle的版本与导入的oracle数据库的版本不一致: 可直接将dmp文件用notepad++打开修改 ...
- django 1.10 CSRF验证失败的解决过程
最近工作闲,没事自学django,感觉这个最烦的就是各版本提供的api函数经常有变化,不是取消了就是参数没有了,网上搜到的帖子也没说明用的是什么版本的django,所以经常出现搬运过来的代码解决不了问 ...
- 微信公众账号 token 验证失败 解决办法
问题:微信公众账号 开发过程中配置 token 提示 验证失败 如下图: 点击修改配置: 填写相关url与token(自定义):点击提交,会出现 出现这种情况,主要是对相关参数不熟悉,要了解url与 ...
- php开发公众号 token验证失败 其中一个原因
断断续续,弄了好几天,索性一狠心花了三个小时,总算找出问题了. "token验证失败" 可能原因有很多种,其他网友已经几乎穷尽了,但是我所遇到的在网络上没有看到,所以这里记录下. ...
- 支付宝即时到账API,网站收到回调页面,输出验证失败,log中responseTxt=错误的问题
看了网上无外乎这三种原因导致: 1.参数编码问题 2.参数多余问题 3.网络延时问题 以上三种可以参考这个网站给出的解决办法:http://blog.csdn.net/zj53hao/article/ ...
随机推荐
- zabbix nagios 类nagios 之 不以性能为前提的开发和监控都是瞎扯淡
从最初的nagios到现在强大的zabbix 3.0,我想说,不以性能为前提的开发和监控都是瞎扯淡? 首先我对这两款监控软件的认识: zabbix,很多企业都在用,给人的感觉是很炫,不过我个人觉得虽然 ...
- TCP协议学习记录 (一) ICMP时间戳请求
程序只实现了获取时间戳,至于将时间戳转换成具体日期和时间,暂时没有好的办法. #define TIME_STAMP_REQUEST 13 struct iphdr { unsigned ; //包头长 ...
- DAP in Coresight
DAP简单来说分成 DP + AP , Debug Port + Access Port Debug Port--和JTAG的接口叫做Debug Port Access Port--和内部其他的接口叫 ...
- Ceph剖析:定时器safetimer的实现
定时器的作用是在指定的时间执行指定的动作.SafeTimer通过multimap数据结构维护定时项,定时项是时间和事件的Pair,定时项在map中按照定时时间从小到大排列.此外,SafeTimer使用 ...
- 服务器内存UDIMM与RDIMM区别
UDIMM 全称是无缓冲双信道内存模块(Unbuffered Dual In-Lne Memory Modules),它不支持服务器内存满配,就是最高容量了,因为使用UDIMM内存时最大使用每通道只能 ...
- linux命令连接远程服务器
ssh root@IP ***连接远程ip cd 路径 ***进入指定路径 ll ***查看当前文件 ll -a ...
- window常用命令
netstat -ano|findstr "端口号" :查看指定端口信息[netstat -ano|findstr "端口号" ] ,然后看下PID号 ...
- flex柱状图
柱状图的展现是通过flex自带的控件实现 控件解析:<mx:ColumnChart id = "columns" dataProvider = "{dataSour ...
- 数据库.bak文件还原报错的处理办法
今天从网上下了个Demo,里面有个.bak文件,就试着还原了一下,结果发现报了错.是了两种方式导入,都不行. 最终找到了解决办法: 可以直接用sql语句对.bak文件进行还原. RESTORE DAT ...
- nodeJS express框架 中文乱码解决办法
最近在研究javascript 的服务端应用 node,之所以想要研究node,是因为前几个月一直在前端挣扎,从javascript入门到在项目中实际使用javascript,确实感悟颇深.javas ...