zoukankan      html  css  js  c++  java
  • 《ASP.NET MVC4 WEB编程》学习笔记------.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返回到客户端。
  • 相关阅读:
    spring-security使用-权限控制(八)
    Spring源码阅读(六)-ConfigurationClassPostProcessor
    java陷阱之应用缓存
    Spring Batch源码阅读-Job之SimpleJob(五)
    Spring Batch源码阅读-JobLauncher之SimpleJobLauncher(四)
    Spring Batch源码阅读-初始化(三)
    Spring Batch源码阅读-简单使用(二)
    Spring Batch源码阅读-基本概念(一)
    JS函数的定义与调用方法
    百度地图离线化(API v=1.3)
  • 原文地址:https://www.cnblogs.com/abc8023/p/3612549.html
Copyright © 2011-2022 走看看