前面介绍了 Action 方法执行过程中的一些主要的组件以及方法执行过程中需要的参数的源数据的提供以及参数的绑定,那些都可以看作是 Action 方法执行前的一些必要的准备工作,接下来便将这些串起来看一下 Action 方法执行的整体的流程。

Action 执行的整体流程

  在接受到客户端的 Http 请求后,Asp.net 的路由系统会对请求信息进行解析得到路由数据,其中包括请求的 ControllerAction 的名称以及当前请求上下文的信息,然后根据这些信息创建对应的Controller 类型实例,调用请求 Action 所对应的 Action 方法对请求进行处理。

  在执行 Action 方法之前需要先执行定义在 Action 上的一些过滤器方法(如认证过滤器、授权过滤器等以及其它的需要在Action 方法执行前调用的过滤器方法),然后对当前要执行的 Action 方法的参数进行赋值,执行 Action 方法并根据执行结果生成 ActionResult 类型的结果,执行所有需要在 Action方法返回前要执行的过滤器方法,执行 ActionResult 并将结果返回给客户端。

  

  前面说过 Action 的执行是通过 IActionInvoker 类型来实现的,该接口中仅定义了一个 bool InvokeAction(ControllerContext context,string actionName) 方法,该方法的实现是通过同步的方式实现的,即具有一个实现类 ControllerActionInvoker,该接口还有一个异步实现的版本 IAsyncActionInvoker,该接口的定义如下示:

 public interface IAsyncActionInvoker : IActionInvoker
{
IAsyncResult BeginInvokeAction(ControllerContext controllerContext, string actionName, AsyncCallback callback, object state);
bool EndInvokeAction(IAsyncResult asyncResult);
}

  从上可以看出,其继承自 IActionInvoker 接口,该接口也具有一个实现类型 AsyncControllerActionInvoker,该接口同时实现了 IActionInvoker 接口 和 IAsyncActionInvoker 接口,同时还继承自 ControllerActionInvoker 类型。

ControllerActionInvoker

  该类型是 IActionInvoker 接口的唯一实现类型,其定义如下:

public class ControllerActionInvoker : IActionInvoker{

	//属性
protected internal ModelBinderDictionary Binders;//改属性值来自于静态类型 ModelBinders的同名属性 //方法 //根据 Action方法的执行结果创建 ActionResult
protected virtual ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue)
} // 获取当前执行的 Action 的描述类 ActionDescriptor
protected virtual ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName); //获取定义在当前 Action 方法上的过滤器信息
protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor); //获取与指定 ParameterDescriptor 相关的的参数的值
protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor); //执行 actionName 对应的 Action方法
public virtual bool InvokeAction(ControllerContext controllerContext, string actionName); //执行 ActionResult
protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult); //执行 Action 上定义的 认证过滤器
protected virtual AuthenticationContext InvokeAuthenticationFilters(ControllerContext controllerContext,IList<IAuthenticationFilter> filters, ActionDescriptor actionDescriptor); //执行 Action 上定义的授权过滤器
protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor); //执行 Action 上定义的异常过滤器
protected virtual ExceptionContext InvokeExceptionFilters(ControllerContext controllerContext, IList<IExceptionFilter> filters, Exception exception);

  下面对几个比较关键的方法着重说明一下:

  其中最为重要的方法当属 InvokeAction,该方法是集中处理 Action 方法执行流程的地方,该方法的实现如下示:

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) {
//controllerContext 为 null,直接抛出异常
if (controllerContext == null) {
throw new ArgumentNullException("controllerContext");
} //actionName 为空且当前Action 没有使用直连路由(特性路由),抛出异常
if (String.IsNullOrEmpty(actionName) && !controllerContext.RouteData.HasDirectRouteMatch())
{
throw new AgumentException(MvcResources.Common_NullOrEmpty, "actionName");
} //定位匹配的 Action 方法
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName); //找到了与请求匹配的 Action 方法
if (actionDescriptor != null) {
//获取定义在当前 Action 上的过滤器方法
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
try {
//执行认证过滤器
AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);
if (authenticationContext.Result != null)
{
//在 IAuthenticationFilter 中定义了两个方法,OnAuthencation 和 OnAuthencationChallenge,其中第二个方法
//在 第一个方法后执行,但其具体的执行时机不是固定的,它可以在认证后执行,可以在授权后执行,亦可以在 action 方法执行后执行,下面的代码逻辑
//便体现了这一点
//http://www.dotnetcurry.com/aspnet-mvc/957/aspnet-mvc-authentication-filters
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext,
filterInfo.AuthenticationFilters, actionDescriptor,authenticationContext.Result);
//执行授权过滤器和认证过滤器任一的验证结果的 ActionResult 不为 null,便会直接执行该 ActionResult,而不去进一步执行Action 方法
InvokeActionResult(controllerContext, challengeContext.Result ? ? authenticationContext.Result);
}
else {
AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
if (authorizationContext.Result != null)
{
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
authorizationContext.Result);
InvokeActionResult(controllerContext, challengeContext.Result ? ? authorizationContext.Result);
}
else //走到这一步表示通过了认证过滤器和授权过滤器
{
//是否对请求的输入内容进行验证,如Html、JavaScript等内容不允许提交等
if (controllerContext.Controller.ValidateRequest)
{
ValidateRequest(controllerContext);
} //获取执行当前 Action所需的参数的值
IDictionary < string, object > parameters = GetParameterValues(controllerContext, actionDescriptor); ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters); //走到这一步,Action 方法已成功执行,针对一些特殊的认证过滤器,即使Action执行成功,仍然需要进行一些处理的
//例如针对不同的用户执行不同反馈操作
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,postActionContext.Result); InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters,
challengeContext.Result ? ? postActionContext.Result);
}
}
} catch (ThreadAbortException) {
throw;
} catch (Exception ex) {
// 发生 Action 过程发生异常,执行异常过滤器
ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
if (!exceptionContext.ExceptionHandled)
{
throw;
}
InvokeActionResult(controllerContext, exceptionContext.Result);
}
return true;
}
//这里表示为找到与请求相匹配的 Action 方法
return false;
}

  我们知道,Action 方法的最终的返回结果一般都是一个 ActionResult 类型的实例,该实例便是根据下面的这个 InvokeActionMethod 方法来创建的,该方法的定义如下示:

protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
{//关于 Execute 方法的详细信息请参考 第一大节
object returnValue = actionDescriptor.Execute(controllerContext, parameters);
ActionResult result = CreateActionResult(controllerContext, actionDescriptor, returnValue);
return result;
}

  其中的 CreateActionResult 方法的实现逻辑特别的简单,就是如果参数 returnValue 为 null,则直接返回一个新的 EmptyResult 类型的对象,如果不为 null,就是用 as 运算符尝试将其转换为一个 ActionResult 类型对象,如果转换成功则返回,否则返回一个新的 ContentResult 类型对象,其中的内容为 returnValue 的字符串形式。在上面的 InvokeAction 方法中有多处调用了 InvokeActionWithFilters 方法,该方法的内部便是调用该方法创建的 ActionResult 类型的对象。

  执行 Action 方法编译生成的委托,根据委托的执行结果生成 ActionResult 类型的实例。至此,一个 Action 方法的执行便完成了,后续的步骤便是调用上一步生成的 ActionResult 实例的 ExecuteResult 方法,将执行的结果响应给客户端。

AsyncControllerActionInvoker

  AsyncControllerActionInvoker 的整体的处理逻辑与 ControllerActionInvoker 保持一致,其不同之处在于其 Action 方法的执行时采用的是异步执行的方式,例如,在前面的 ControllerDescriptorInvokeAction 方法中使用的 ControllerDescriptor 的类型实例是 ReflectedControllerDescriptorActionDescriptor 类型实例是 ReflectedActionDescriptor,而在该类型中使用的便是其一步的版本,ReflectedAsyncControllerDescriptorAsyncReflectedActionDescriptor

  

  至此,关于 Action 方法的执行,介绍完毕。

Asp.net mvc 中Action 方法的执行(三)的更多相关文章

  1. Asp.net mvc 中Action 方法的执行(二)

    [toc] 前面介绍了 Action 执行过程中的几个基本的组件,这里介绍 Action 方法的参数绑定. 数据来源 为 Action 方法提供参数绑定的原始数据来源于当前的 Http 请求,可能包含 ...

  2. Asp.net mvc 中Action 方法的执行(一)

    [toc] 在 Aps.net mvc 应用中对请求的处理最终都是转换为对某个 Controller 中的某个 Action 方法的调用,因此,要对一个请求进行处理,第一步,需要根据请求解析出对应的 ...

  3. C# MVC 用户登录状态判断 【C#】list 去重(转载) js 日期格式转换(转载) C#日期转换(转载) Nullable<System.DateTime>日期格式转换 (转载) Asp.Net MVC中Action跳转(转载)

    C# MVC 用户登录状态判断   来源:https://www.cnblogs.com/cherryzhou/p/4978342.html 在Filters文件夹下添加一个类Authenticati ...

  4. windows server 证书的颁发与IIS证书的使用 Dapper入门使用,代替你的DbSQLhelper Asp.Net MVC中Action跳转(转载)

    windows server 证书的颁发与IIS证书的使用   最近工作业务要是用服务器证书验证,在这里记录下一. 1.添加服务器角色 [证书服务] 2.一路下一步直到证书服务安装完成; 3.选择圈选 ...

  5. Asp.Net MVC中Action跳转(转载)

    首先action的跳转大致归类: 1跳转到与当前同一控制器内的action和不同控制器内的action. 2带有参数的action跳转和不带参数的action跳转. 3跳转到指定视图,不经过Contr ...

  6. Asp.Net MVC中Action跳转

    首先action的跳转大致归类: 1跳转到与当前同一控制器内的action和不同控制器内的action. 2带有参数的action跳转和不带参数的action跳转. 3跳转到指定视图,不经过Contr ...

  7. Asp.Net MVC中Action跳转小结

    首先我觉得action的跳转大致可以这样归一下类,跳转到同一控制器内的action和不同控制器内的action.带有参数的action跳转和不带参数的action跳转. 一.RedirectToAct ...

  8. asp.net mvc中action接收客户端发送过来的html片段

    出于安全的考虑,默认情况下,如果从客户端发送过来的数据中直接包括了HTML内容,ASP.NET会自动启动保护措施,这当然是一个比较好的设计,只不过在某种情况下我们真的需要获取这个值,那我们应该怎么办呢 ...

  9. asp.net mvc中动作方法的重定向

    简单介绍一下mvc控制器下怎样重定向的其它页面 1.方式1:Response.Redirect重定向 //Response.Redirect方式跳转 Response.Redirect("~ ...

随机推荐

  1. grep命令及基本正则表达式

    grep命令是Linux系统中一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来. grep可用于shell脚本,因为grep通过返回一个状态值来说明搜索的状态,如果模板搜索成功 ...

  2. python的sorted函数

    sorted很简单,没太多好写的 ,只是给自己做个笔记. sorted接受三个参数,返回一个排序之后的list. 第一个接受一个可迭代的对象(因为sorted实现了迭代协议,所以接受的参数不一定需要l ...

  3. 【原创】Android AOP面向切面编程AspectJ

    一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...

  4. Hibernate学习笔记(6)---Criteria接口

    Criteria接口 Criteria查询通过面相对向的设计,将数据查询条件封装为一个对象.在hibernate执行时会把criteria指定的查询恢复相应的sql语句. 条件查询 Criteria ...

  5. PyCharm/WebStorm遇到Cannot start internal HTTP server

    在开始学习html.css的时候,使用PyCharm 的模拟链接到服务器的时候总是遇到 网上也没有遇到合适的解决方案,遂下载了WebStorm,希望能通过安装配置好一些设置,结果依然不行,只有从头分析 ...

  6. docker:(4)利用WebHook实现持续集成

    研发小伙伴可能对下列操作步骤会深有体会 写代码-->提交代码-->打包-->发布 在项目调试测试阶段,可能经常需要重复上面的步骤,以便将最新代码部署到特定环境供测试人员或其他人员使用 ...

  7. Flask基础

    简介 Flask是当下流行的Web框架,它是用Python实现的.Flask显著的特点是:它是一个“微”框架.”微”意味着Flask旨在保持核心的简单,但同时又易于扩展.默认情况下,Flask 不包含 ...

  8. ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入)

    前言 本文主要是详解一下在ASP.NET Core中,自带的IOC容器相关的使用方式和注入类型的生命周期. 这里就不详细的赘述IOC是什么 以及DI是什么了.. emm..不懂的可以自行百度. 目录 ...

  9. C# 实现邮件发送

    要实现邮件发送功能首先需要准备两三个邮箱测试,在这里呢准备了2个QQ邮箱和一个微软邮箱,详细请看代码. 我这里是使用QQ邮箱向另外两个邮箱发送邮件的,在开始写代码之前你需要登录你QQ邮箱进行以下几个操 ...

  10. MYSQL的REPLACE和ON DUPLICATE KEY UPDATE使用

    REPLACE 我们在使用数据库时可能会经常遇到这种情况.如果一个表在一个字段上建立了唯一索引,当我们再向这个表中使用已经存在的键值插入一条记录,那将会抛出一个主键冲突的错误.当然,我们可能想用新记录 ...