对InvokeAction简略分析了解验证失败为什么Action还会继续执行
2016-03-18 20:09
627 查看
一、前言
有些同学使用AuthorizationFilter来进行用户是否登录验证,如果未登录就跳到登录页。
很简单的一个场景,但是有些同学会发现虽然验证失败了,但是整个Action还会执行一遍。
于是google啊google啊,然后找到了解决方案,但是不知为啥,下面就对InvokeAction的简略分析,说说到底是为啥。
二、分析InvokeAction执行顺序
1、首先获取Controller和Action描述类
2、大家都知道每个Action执行前会先执行一些Filters,所以首先需要获取所有FilterScope为Action的Filters
3、Filter的执行是有顺序的,AuthenticationFilter最先执行,接下来就是执行AuthenticationFilter的代码
这里要注意,AuthenticationContext有个属性Result,如果验证失败一定要设置这个属性的值,否则看看下面的代码就知道了。
看到了吧,如果你设置Result的值,此时就会执行你设置的Result,如果不设置就会继续走下去,会走哪去,一直往后看,你就知道了。
插一下,我们先看看 InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)是什么东东。
这个方法看字面意思是执行一个ActionResult,还是看两个简单的例子吧。
(1)ContentResult
很简单吧,就是把你设置的Content输出到页面中,用的也是response.Write(content)方法,所以在Mvc中完全可以用ContentResult替换response.Write(content).
(2)HttpStatusCodeResult
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,否则就继续往下执行。
看到这里,是不是有些同学会想:“原来如此啊”。
三、总结
由于微软没有提供AuthenticationFilter对应的基类,只提供了一个IAuthenticationFilter,所以我们就用AuthorizeAttribute来进行总结。
一些同学自定义AuthorizationFilters的时候,代码结尾没有调用基类的 OnAuthorization,也没有设置FilterContext的Result属性,只是进行Response.Redirect或者其他认为跳出Action执行的代码。这样其实等于没有进行验证,Action中的代码还是会执行。
在进行AuthorizationFilters的时候,如果想验证失败之后,不执行Action的方法,可以有以下两种方法:
(1)调用基类的 OnAuthorization,我们看看基类 OnAuthorization 都干了啥?
HttpUnauthorizedResult:这个类有没有看着眼熟,对了,就是前面讲Result中的第二个例子的子类。
(2)给filterContext的Result属性设置一个Result,即使是EmptyResult都可以,如果你想输出点内容到页面中,那就使用ContetResult,前面说过它是通过调用Response.Write()来实现的。
最后附上ControllerActionInvoker:InvokeAction(L230)方法的详细内容:
View Code
有些同学使用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;
}
View Code
相关文章推荐
- [扫描线 计算几何] BZOJ 1845 [Cqoi2005] 三角形面积并
- BestCoder Round #75 T3 King's Order HDOJ 5642
- 1. 动态指定编译(运行)路径
- Java中线程池的使用
- 梦幻判断条件
- 蓝桥杯比赛总结
- 还是畅通工程
- OJ831 统计good及相关题
- HashMap扩容机制、线程安全
- 条理清晰的搭建SSH环境之添加所需jar包
- paper 45:latex的使用
- Android课程---Activity 的生命周期
- centos7 安装ftp服务
- poj 3159 Candies (spfa+stack)
- poj 1002 487-3279(参考YSQ)
- Android小程序-标准体重计算器
- android dispatchTouchEvent方法查找包含点击坐标的view;
- 2015 房子距离
- Javascript 执行上下文 context&scope
- 【Chromium中文文档】跨进程通信 (IPC)