了解.net mvc实现原理ActionResult/View

ActionResult
ActionResult是Action的返回结果。ActionResult 有多个派生类,每个子类功能均不同,并不是所有的子类都需要返回视图View,有些直接返回流,有些返回字符串等。我们来看一下ActionResult派生类关系图
具体看一下每个类的功能,由于MSDN的示意图太简单不能完全表现所有的子类功能
类名 抽象类 父类 功能
 ActionResult  abstract  Object 顶层父类
ContentResult     根据内容的类型和编码,数据内容.通过Controller的Content方法返回
EmptyResult     返回空结果
FileResult abstract   写入文件内容,具体的写入方式在派生类中.
FileContentResult   FileResult 通过 文件byte[] 写入Response 返回客户端,Controller的File方法
FilePathResult   FileResult 通过 文件路径 写入Response 返回客户端,Controller的File方法
FileStreamResult   FileResult 通过 Stream 写入Response 返回客户端,Controller的File方法
HttpUnauthorizedResult     抛出401错误
JavaScriptResult     返回javascript文件
JsonResult     返回Json格式的数据
RedirectResult     使用Response.Redirect重定向页面
RedirectToRouteResult     根据Route规则重定向页面
ViewResultBase abstract   调用IView.Render() 返回视图,两个常用属性ViewData,TempData
PartialViewResult   ViewResultBase 调用父类ViewResultBase 的ExecuteResult方法. 
重写了父类的FindView方法. 
寻找用户控件.ascx文件
ViewResult   ViewResultBase

调用父类ViewResultBase 的ExecuteResult方法. 
重写了父类的FindView方法. 
寻找视图页面(aspx,cshtml或自定义视图)

Controller的View()方法默认封装ViewResult返回结果

简单的列几种写法,都是Controller已经封装好的
 public ActionResult ShowContent()
        {
            return Content("测试ContentResult方法");  //默认封装ContentResult文本返回
        }

        public ActionResult Index(UserVO userVo)
        { 
            return View();   //默认封装ViewResult返回   
        }

        public ActionResult DownLoadFile(string fileName)
        {
            return File(Server.MapPath(@"/Images/view.jpg"), @"image/gif");
        }

        public ActionResult ToOther(string fileName)
        {
            return Redirect(@"http://localhost:1847/Menu/ShowContent");
        }
当然你可以自己实现每种的类型返回,而不是通过Controller的方法返回。这个环节最重要的问题,当Action返回ActionResult后,这个ActionResult是如何工作的?ActionResult只有一个抽象方法 ExecuteResult ,当ActionResult实例被返回后,Controller执行器ControllerActionInvoker的InvokeAction方法在处理完IActionFilter之后调用了这段代码InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
看后两个参数,一个是IResultFilter过滤器,一个是Action返回的Result。该方法对返回ActionResult进行前置拦截后,接着调用ActionResult的ExecuteResult方法去处对应的响应业务(返回视图,或字符串,文件流等),最后又对ActionResult后置拦截了一次。调用棧比较深
 
//1---------------------------------  
protected virtual ResultExecutedContext InvokeActionResultWithFilters(ControllerContext controllerContext, IList<IResultFilter> filters, ActionResult actionResult) {
            ResultExecutingContext preContext = new ResultExecutingContext(controllerContext, actionResult);
//InvokeActionResult 做为委托被前置与后置包围了
            Func<ResultExecutedContext> continuation = delegate {
                InvokeActionResult(controllerContext, actionResult);
                return new ResultExecutedContext(controllerContext, actionResult, false /* canceled */, null /* exception */);
            };

            // need to reverse the filter list because the continuations are built up backward
            Func<ResultExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
                (next, filter) => () => InvokeActionResultFilter(filter, preContext, next));
            return thunk();
        }
//2--------------------------------------------
internal static ResultExecutedContext InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func<ResultExecutedContext> continuation) {
            filter.OnResultExecuting(preContext);  //前置拦截----------------------------------------
            if (preContext.Cancel) {
                return new ResultExecutedContext(preContext, preContext.Result, true /* canceled */, null /* exception */);
            }

            bool wasError = false;
            ResultExecutedContext postContext = null;
            try {
                postContext = continuation();    //ActionResult的ExecuteResult的调用环节------------------------------------------
            }
            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.
                postContext = new ResultExecutedContext(preContext, preContext.Result, false /* canceled */, null /* exception */);
                filter.OnResultExecuted(postContext);   //出错了,后置拦截----------------------------------
                throw;
            }
            catch (Exception ex) {
                wasError = true;
                postContext = new ResultExecutedContext(preContext, preContext.Result, false /* canceled */, ex);
                filter.OnResultExecuted(postContext);
                if (!postContext.ExceptionHandled) {
                    throw;
                }
            }
            if (!wasError) {
                filter.OnResultExecuted(postContext);    //后置拦截----------------------------------------------
            }
            return postContext;
        }
//3------------------------------------------------------------------
 protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) {
            actionResult.ExecuteResult(controllerContext);
        }
View Code

 

注释的地方注意看一下,InvokeActionResult 是调用ActionResult.ExecuteResult的方法,被做为委托放到IResultFilter前后拦截法中间执行。Controller的执行器ControllerActionInvoker 这几个环节的调度者。再看一下ActionResult.ExecuteResult方法的业务,挑选个有代表性的子类实现的业务贴上来,
FileResult基类 的ExecuteResult,但又调用了WriteFile方法,这个方法又下放到子类中实现了
ublic override void ExecuteResult(ControllerContext context) {
            if (context == null) {
                throw new ArgumentNullException("context");
            }

            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = ContentType;

            if (!String.IsNullOrEmpty(FileDownloadName)) {
                // From RFC 2183, Sec. 2.3:
                // The sender may want to suggest a filename to be used if the entity is
                // detached and stored in a separate file. If the receiving MUA writes
                // the entity to a file, the suggested filename should be used as a
                // basis for the actual filename, where possible.
                string headerValue = ContentDispositionUtil.GetHeaderValue(FileDownloadName);
                context.HttpContext.Response.AddHeader("Content-Disposition", headerValue);
            }

            WriteFile(response);
        }
View Code

 

我们挑选FileStreamResult子类的WriteFile方法看看
protected override void WriteFile(HttpResponseBase response) {
            // grab chunks of data and write to the output stream
            Stream outputStream = response.OutputStream;
            using (FileStream) {
                byte[] buffer = new byte[_bufferSize];

                while (true) {
                    int bytesRead = FileStream.Read(buffer, 0, _bufferSize);
                    if (bytesRead == 0) {
                        // no more data
                        break;
                    }

                    outputStream.Write(buffer, 0, bytesRead);
                }
            }
        }
View Code

 

整个过程看下来FileStreamResult的ExecuteResult 将文件流写入HttpResponse中返回到客户端,而并不是返回视图。再看一下ViewResult的ExecuteResult的业务,这个业务是在父类的中实现的
ViewResultBase的ExecuteResult业务,ViewResultBase还有两个重要的性ViewData,TempData是在Acion返回的时候封装好的。
public override void ExecuteResult(ControllerContext context) {
            if (context == null) {
                throw new ArgumentNullException("context");
            }
            if (String.IsNullOrEmpty(ViewName)) {
                ViewName = context.RouteData.GetRequiredString("action");
            }

            ViewEngineResult result = null;

            if (View == null) {
                result = FindView(context);
                View = result.View;
            }

            TextWriter writer = context.HttpContext.Response.Output;
            ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);
            View.Render(viewContext, writer);

            if (result != null) {
                result.ViewEngine.ReleaseView(context, View);
            }
        }
View Code

 

 
先根据上下文中的路由+Action名找到对应的IView,然后调用IView的Render会出视图写入context.HttpContext.Response.Output返回到客户端。

了解.net mvc实现原理ActionResult/View,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。