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方法. 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); }
注释的地方注意看一下,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); }
我们挑选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); } } }
整个过程看下来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); } }
先根据上下文中的路由+Action名找到对应的IView,然后调用IView的Render会出视图写入context.HttpContext.Response.Output返回到客户端。