一、概述

  本节来看一下ASP.NET MVC【View的呈现】的内容,View的呈现是在Action执行之后进行,Action的执行生成一个ActionResult,【View的呈现】的功能就是:通过InvokeActionResult方法对【Action的执行】中生成的ActionResult进行处理。(ActionResult泛指那些继承自抽象类System.Web.Mvc.ActonResult的类的实例)

  为了会纵观【View的呈现】在全局中的位置,下面我们再来回顾下处理请求的整个流程:在此系列开篇的时候介绍了MVC的生命周期 , 对于ASP.NET和ASP.NET MVC,都是将相应的类的方法注册到HttpApplication事件中,通过事件的依次执行从而完成对请求的处理。而针对MVC,请求是先 经过路由系统,然后由一个MvcHandler来处理的,当请求到来时,执行此MvcHandler的ProcessRequest方法(因为已将 MvcHandler类的ProcessRequest方法注册到HttpApplication的事件中,所以事件的执行就触发了此方法),下图就是一个简要的执行过程!

  1. public class ControllerActionInvoker : IActionInvoker
  2. {
  3. protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
  4. {
  5. actionResult.ExecuteResult(controllerContext);
  6. }
  7. }

  整个过程大致经过【Controller的激活】-->【Action的执行】-->【View的呈现】,由上图可知,【View的呈现】是由ControllerActionInvoker类中的InvokeActionResult方法来触发的!

二、ActionResult的创建

  概述中提到,【View的呈现】的功能就是:通过InvokeActionResult方法对【Action的执行】中生成的ActionResult进行处理。即:ActionResult是在【Action的执行】中创建的,创建方式有:

  • 请求没有通过Action的过滤器时,在过滤器的方法中创建一个ActionResult,将其当作最终的ActionResult,进行View的呈现
  • 请求通过所有过滤器,将Action方法返回的ActionResult当作最终的ActionResult,进行View的呈现。
    注:在Action方法中其实调用Controller类中的方法来进行创建ActionResult实例的,如:return Content("OK");等同于return new ContentResult(){ Content="OK"};

例、自定义个Action过滤器,当没有通过时按照过滤器中定义的ActionResult进行View的呈现,具体执行过程下一部分介绍!

  1. public class MyActionFilter:FilterAttribute,IActionFilter
  2. {
  3. public void OnActionExecuted(ActionExecutedContext filterContext)
  4. {
  5. }
  6.  
  7. public void OnActionExecuting(ActionExecutingContext filterContext)
  8. {
  9. if (filterContext.RouteData.DataTokens["OOK"] != "WuPeiqi")
  10. {
  11. ContentResult contentResult = new ContentResult();
  12. contentResult.Content = "DataToKens值有误";
  13. filterContext.Result = contentResult;
  14. }
  15. }
  16. }
  17. //将此过滤器应用的Action上,那么当请求中DataTokens的值不是不是相应的值时,就会用过滤器中的ContentResult对象来进行View的呈现,否则,就是利用Action方法Index中创建的ActionResult进行View的呈现!
  18. public class HomeController : Controller
  19. {
  20. [MyActionFilter]
  21. public ActionResult Index()
  22. {
  23. return Content("正确");
  24. }
  25. }

三、View呈现过程分析

  ASP.NET MVC的【View的呈现】其实就是执行ActonResult的ExcuteResult方法!而接下来我们介绍的就是这个ExcuteResult方法触发了那些操作!!!在介绍之前我们先来看看微软提供了那些ActionResult!(ActionResult泛指那些继承自System.Web.Mvc.ActionResult的类)

  1. public abstract class ActionResult
  2. {
  3. public abstract void ExecuteResult(ControllerContext context);
  4. }

基类System.Web.Mvc.ActionResult

  • EmptyResult
  • ContentResult
  • FileResult
  • JavaScriptResult
  • JsonResult
  • HttpStatusCodeResult
  • RedirectResult
  • RedirectToRouteResult
  • ViewResult

  在ASP.NET MVC 的【Action的执行】中创建以上任意一个ActionResult对象,并执行该对象的ExcuteResult方法,从而进行【View的呈现】。这里的最后一项ViewResult比较特殊,它的处理流程相对复杂,涉及到Razor引擎什么的,之后详细介绍!

下面就来看一些以上ActionResult的源码,了解下【View的呈现】如何实现!

1、EmptyResult

  1. public class EmptyResult : ActionResult
  2. {
  3. private static readonly EmptyResult _singleton = new EmptyResult();
  4.  
  5. internal static EmptyResult Instance
  6. {
  7. get { return _singleton; }
  8. }
  9.  
  10. public override void ExecuteResult(ControllerContext context)
  11. {
  12. }
  13. }

  由EmptyResult源码可见,其ExecuteReuslt方法什么都没做,也就是该ActionReuslt的【View的呈现】部分不做任何操作,那么此流程也就执行完毕。再看概述中的图可知,接下来进行【对TempData再一次处理】-->【释放Controller对象】,之后再继续HttpApplication其他的事件,包括对Session的处理、缓存的处理、对请求的返回等。

 2、ContentResult

  ContentResult用于将字符串响应给客户端!

  1. public class ContentResult : ActionResult
  2. {
  3. public string Content { get; set; }
  4.  
  5. public Encoding ContentEncoding { get; set; }
  6.  
  7. public string ContentType { get; set; }
  8.  
  9. public override void ExecuteResult(ControllerContext context)
  10. {
  11. if (context == null)
  12. {
  13. throw new ArgumentNullException("context");
  14. }
  15.  
  16. HttpResponseBase response = context.HttpContext.Response;
  17.  
  18. if (!String.IsNullOrEmpty(ContentType))
  19. {
  20. response.ContentType = ContentType;
  21. }
  22. if (ContentEncoding != null)
  23. {
  24. response.ContentEncoding = ContentEncoding;
  25. }
  26. if (Content != null)
  27. {
  28. response.Write(Content);
  29. }
  30. }
  31. }

  上述context.HttpContext.Response得到的是一个HttpResponseWrapper类型的对象response,该对象内有一个HttpResponse类型的私有变量_httpResponse,对于该HttpResponseWrapper对象的属性和方法其实都是执行私有变量_httpResponse对应的属性和方法!
  由于HttpResponseWrapper对象属性和方法都是对私有变量_httpResponse的相关操作,而查看HttpResponseWrapper类部分源代码,_httpResponse变量是通过构造函数赋值的,而该构造函数的参数值是怎么来的呢?是在HttpApplication事件之前,通过HttpRuntime类创建请求上下文HttpContext对象时,又触发创建了HttpResponse对象并赋值到请求上下文HttpContext对象的一个私有变量中保存着的!
  又由于HttpResponse对象的属性和方法又都是对私有变量_writer的相关操作,再看HttpResponse类的源代码,它的Write的方法其实是执行其TextWriter类型的私有变量_writer的Write方法,而该私有变量_writer是怎么来的呢?是在HttpApplication事件之前,通过HttpRuntime类创建请求上下文HttpContext对象时,触发创建了HttpResponse对象,之后又初始化HttpResponse对象的_writer字段为一个HttpWriter对象。
  最终,执行HttpWriter对象的Write方法,根据ContentType定义的媒体类型和ContentEncoding定义的编码方法将字符串发送到 HTTP 输出流。ContentType定义的是MIME类型(默认为”text/html"),ContentEncoding定义的编码方式(默认是操作系统的当前 ANSI 代码页的编码System.Text.Encoding.Default)。

  1. public class HttpResponseWrapper : HttpResponseBase
  2. {
  3. private HttpResponse _httpResponse;
  4. //设置或获取响应内容的编码类型
  5. public override Encoding ContentEncoding
  6. {
  7. get
  8. {
  9. return this._httpResponse.ContentEncoding;
  10. }
  11. set
  12. {
  13. this._httpResponse.ContentEncoding = value;
  14. }
  15. }
  16.  
  17. public override string ContentType
  18. {
  19. get
  20. {
  21. return this._httpResponse.ContentType;
  22. }
  23. set
  24. {
  25. this._httpResponse.ContentType = value;
  26. }
  27. }
  28. public override void Write(string s)
  29. {
  30. this._httpResponse.Write(s);
  31. }
  32. }

HttpResponseWrapper

  1. public sealed class HttpResponse
  2. {
  3. private TextWriter _writer;
  4. private Encoding _encoding;
  5. private string _contentType = "text/html";
  6.  
  7. public Encoding ContentEncoding
  8. {
  9. get
  10. {
  11. if (this._encoding == null)
  12. {
  13. //获取webconfig文件中,globalization节点的值
  14. GlobalizationSection globalization = RuntimeConfig.GetLKGConfig(this._context).Globalization;
  15. if (globalization != null)
  16. {
  17. //设置Http响应的内容编码
  18. this._encoding = globalization.ResponseEncoding;
  19. }
  20. //没有在globalization节点中配置编码类型
  21. if (this._encoding == null)
  22. {
  23. //获取操作系统的当前 ANSI 代码页的编码并赋值给Http响应内容的编码
  24. this._encoding = Encoding.Default;
  25. }
  26. }
  27. return this._encoding;
  28. }
  29. set
  30. {
  31. if (value == null)
  32. {
  33. throw new ArgumentNullException("value");
  34. }
  35. //当没有设置编码类型或者编码类型和原来的不相同时,根据value重新设定编码类型
  36. if (this._encoding == null || !this._encoding.Equals(value))
  37. {
  38. this._encoding = value;
  39. this._encoder = null;
  40. if (this._httpWriter != null)
  41. {
  42. //将HttpResponse类中与编码相关的属性值赋值到HttpWriter对象中与编码相关的属性
  43. //以便HttpWriter输出响应流时按照此编码进行
  44. this._httpWriter.UpdateResponseEncoding();
  45. }
  46. }
  47. }
  48. }
  49.  
  50. public string ContentType
  51. {
  52. get
  53. {
  54. return this._contentType;
  55. }
  56. set
  57. {
  58. if (!this._headersWritten)
  59. {
  60. this._contentTypeSetByManagedCaller = true;
  61. this._contentType = value;
  62. return;
  63. }
  64. if (this._contentType == value)
  65. {
  66. return;
  67. }
  68. throw new HttpException(SR.GetString("Cannot_set_content_type_after_headers_sent"));
  69. }
  70. }
  71.  
  72. public void Write(string s)
  73. {
  74. this._writer.Write(s);
  75. }
  76. }

HttpResponse

  1. public sealed class HttpWriter : TextWriter
  2. {
  3. //根据编码规则将字符串发送到 HTTP 输出流
  4. public override void Write(string s)
  5. {
  6. if (this._ignoringFurtherWrites)
  7. {
  8. return;
  9. }
  10. if (s == null)
  11. {
  12. return;
  13. }
  14. if (s.Length != )
  15. {
  16. if (s.Length < this._charBufferFree)
  17. {
  18. StringUtil.UnsafeStringCopy(s, , this._charBuffer, this._charBufferLength - this._charBufferFree, s.Length);
  19. this._charBufferFree -= s.Length;
  20. }
  21. else
  22. {
  23. int i = s.Length;
  24. int num = ;
  25. while (i > )
  26. {
  27. if (this._charBufferFree == )
  28. {
  29. this.FlushCharBuffer(false);
  30. }
  31. int num2 = (i < this._charBufferFree) ? i : this._charBufferFree;
  32. StringUtil.UnsafeStringCopy(s, num, this._charBuffer, this._charBufferLength - this._charBufferFree, num2);
  33. this._charBufferFree -= num2;
  34. num += num2;
  35. i -= num2;
  36. }
  37. }
  38. }
  39. if (!this._responseBufferingOn)
  40. {
  41. //将信息写入 HTTP 响应输出流。
  42. this._response.Flush();
  43. }
  44. }
  45. //更新编码相关的字段
  46. internal void UpdateResponseEncoding()
  47. {
  48. if (this._responseEncodingUpdated && this._charBufferLength != this._charBufferFree)
  49. {
  50. this.FlushCharBuffer(true);
  51. }
  52. this._responseEncoding = this._response.ContentEncoding;
  53. this._responseEncoder = this._response.ContentEncoder;
  54. this._responseCodePage = this._responseEncoding.CodePage;
  55. this._responseCodePageIsAsciiCompat = CodePageUtils.IsAsciiCompatibleCodePage(this._responseCodePage);
  56. this._responseEncodingUpdated = true;
  57. }
  58.  
  59. }

HttpWriter

在ASP.NET MVC 的Controller类中提供了以下三个创建ContentResult的重载,当然也可以直接在Action中创建ContentReuslt对象并作为方法的返回值。

  1. public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
  2. {
  3. //省略其他方法
  4. protected internal ContentResult Content(string content)
  5. {
  6. return Content(content, null /* contentType */);
  7. }
  8.  
  9. protected internal ContentResult Content(string content, string contentType)
  10. {
  11. return Content(content, contentType, null /* contentEncoding */);
  12. }
  13.  
  14. protected internal virtual ContentResult Content(string content, string contentType, Encoding contentEncoding)
  15. {
  16. return new ContentResult
  17. {
  18. Content = content,
  19. ContentType = contentType,
  20. ContentEncoding = contentEncoding
  21. };
  22. }
  23. }

Controller

扩展:请求上下文HttpContext、HttpResponse、HttpRequest创建流程
  当请求到达IIS,IIS根据请求的后缀名判断是否加载aspnet_isapi.dll,一旦工作进程加载了aspnet_isapi.dll,就会加载IsapiRuntime,被加载的IsapiRuntime会接管Http请求,之后IsapiRuntime执行其方法ProcessRequest(IntPtr ecb, int iWRType),该方法实现从ISAPI扩展控制块(ECB)中获取当前Http请求相关信息并封装到IsapiWorkrRequest对象中。然后将该对象传递给HttpRuntime,通过该类中的ProcessRequestInternal()方法创建HttpContext类实例,进入ProcessRequestInternal方法之后,内部触发一系列的方法,最终创建一个HttpContent实例(可通过HttpContent.Current获取到这个实例),且该实例会在整个生命周期内存活。创建HttpContext对象时,同时也创建了HttpRequest和HttpResponse对象,并赋值到私有字段中,通过公有属性去获取这两个对象。

  之后HttpRuntime会向HttpApplicationFactory类 提出请求,要求返回一个HttpApplication对象,HttpApplicationFactory在收到请求之后会检查是否有已经存在并且空闲的对象,如果有就取出一个HttpApplication对象返回给HttpRuntime类,如果没有,则要创建一个给HttpRuntime。

  1. public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IISAPIRuntime2, IRegisteredObject
  2. {
  3. public ISAPIRuntime()
  4. {
  5. //将该ISAPIRuntime对象放在应用程序的已注册对象列表中
  6. HostingEnvironment.RegisterObject(this);
  7. }
  8.  
  9. public int ProcessRequest(IntPtr ecb, int iWRType)
  10. {
  11. IntPtr intPtr = IntPtr.Zero;
  12. if (iWRType == )
  13. {
  14. intPtr = ecb;
  15. ecb = UnsafeNativeMethods.GetEcb(intPtr);
  16. }
  17. ISAPIWorkerRequest iSAPIWorkerRequest = null;
  18. int result;
  19. try
  20. {
  21. bool useOOP = iWRType == ;
  22. //将ISAPI扩展控制块(ECB)中Http请求相关的信息封装到IsapiWorkerRequest对象中
  23. iSAPIWorkerRequest = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
  24. iSAPIWorkerRequest.Initialize();
  25. string appPathTranslated = iSAPIWorkerRequest.GetAppPathTranslated();
  26. string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
  27. if (appDomainAppPathInternal == null || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
  28. {
  29. //ASP.NET运行时开始执行
  30. HttpRuntime.ProcessRequestNoDemand(iSAPIWorkerRequest);
  31. result = ;
  32. }
  33. else
  34. {
  35. HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[]
  36. {
  37. appDomainAppPathInternal,
  38. appPathTranslated
  39. }));
  40. result = ;
  41. }
  42. }
  43. //省略部分代码
  44. return result;
  45. }
  46. }

ISAPIRuntime

  1. public sealed class HttpRuntime
  2. {
  3. //静态字段
  4. private static HttpRuntime _theRuntime;
  5. public HttpRuntime()
  6. {
  7. }
  8. //静态构造函数
  9. static HttpRuntime()
  10. {
  11. HttpRuntime.s_autogenKeys = new byte[];
  12. HttpRuntime.DirectorySeparatorString = new string(Path.DirectorySeparatorChar, );
  13. HttpRuntime.DoubleDirectorySeparatorString = new string(Path.DirectorySeparatorChar, );
  14. HttpRuntime.s_InvalidPhysicalPathChars = new char[]
  15. {
  16. '/',
  17. '?',
  18. '*',
  19. '<',
  20. '>',
  21. '|',
  22. '"'
  23. };
  24. HttpRuntime.s_initialized = false;
  25. HttpRuntime.s_isEngineLoaded = false;
  26. HttpRuntime.s_factoryLock = new object();
  27. HttpRuntime.AddAppDomainTraceMessage("*HttpRuntime::cctor");
  28. HttpRuntime.StaticInit();
  29. HttpRuntime._theRuntime = new HttpRuntime();
  30. HttpRuntime._theRuntime.Init();
  31. HttpRuntime.AddAppDomainTraceMessage("HttpRuntime::cctor*");
  32. }
  33.  
  34. internal static void ProcessRequestNoDemand(HttpWorkerRequest wr)
  35. {
  36. RequestQueue requestQueue = HttpRuntime._theRuntime._requestQueue;
  37. wr.UpdateInitialCounters();
  38. if (requestQueue != null)
  39. {
  40. wr = requestQueue.GetRequestToExecute(wr);
  41. }
  42. if (wr != null)
  43. {
  44. HttpRuntime.CalculateWaitTimeAndUpdatePerfCounter(wr);
  45. wr.ResetStartTime();
  46. //继续执行
  47. HttpRuntime.ProcessRequestNow(wr);
  48. }
  49. }
  50. internal static void ProcessRequestNow(HttpWorkerRequest wr)
  51. {
  52. //继续执行
  53. HttpRuntime._theRuntime.ProcessRequestInternal(wr);
  54. }
  55.  
  56. private void ProcessRequestInternal(HttpWorkerRequest wr)
  57. {
  58. Interlocked.Increment(ref this._activeRequestCount);
  59. if (this._disposingHttpRuntime)
  60. {
  61. try
  62. {
  63. wr.SendStatus(, "Server Too Busy");
  64. wr.SendKnownResponseHeader(, "text/html; charset=utf-8");
  65. byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>");
  66. wr.SendResponseFromMemory(bytes, bytes.Length);
  67. wr.FlushResponse(true);
  68. wr.EndOfRequest();
  69. }
  70. finally
  71. {
  72. Interlocked.Decrement(ref this._activeRequestCount);
  73. }
  74. return;
  75. }
  76. HttpContext httpContext;
  77. try
  78. {
  79. //创建请求上下文,继续执行
  80. httpContext = new HttpContext(wr, false);
  81. }
  82. catch
  83. {
  84. try
  85. {
  86. wr.SendStatus(, "Bad Request");
  87. wr.SendKnownResponseHeader(, "text/html; charset=utf-8");
  88. byte[] bytes2 = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
  89. wr.SendResponseFromMemory(bytes2, bytes2.Length);
  90. wr.FlushResponse(true);
  91. wr.EndOfRequest();
  92. return;
  93. }
  94. finally
  95. {
  96. Interlocked.Decrement(ref this._activeRequestCount);
  97. }
  98. }
  99. wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, httpContext);
  100. HostingEnvironment.IncrementBusyCount();
  101. try
  102. {
  103. try
  104. {
  105. this.EnsureFirstRequestInit(httpContext);
  106. }
  107. catch
  108. {
  109. if (!httpContext.Request.IsDebuggingRequest)
  110. {
  111. throw;
  112. }
  113. }
  114. //初始化HttpResponse的TextWriter
  115. httpContext.Response.InitResponseWriter();
  116. //通过 HttpApplicationFactory获取HttpApplication实例
  117. IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(httpContext);
  118. if (applicationInstance == null)
  119. {
  120. throw new HttpException(SR.GetString("Unable_create_app_object"));
  121. }
  122. if (EtwTrace.IsTraceEnabled(, ))
  123. {
  124. EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, httpContext.WorkerRequest, applicationInstance.GetType().FullName, "Start");
  125. }
  126. if (applicationInstance is IHttpAsyncHandler)
  127. {
  128. IHttpAsyncHandler httpAsyncHandler = (IHttpAsyncHandler)applicationInstance;
  129. httpContext.AsyncAppHandler = httpAsyncHandler;
  130. httpAsyncHandler.BeginProcessRequest(httpContext, this._handlerCompletionCallback, httpContext);
  131. }
  132. else
  133. {
  134. applicationInstance.ProcessRequest(httpContext);
  135. this.FinishRequest(httpContext.WorkerRequest, httpContext, null);
  136. }
  137. }
  138. catch (Exception e)
  139. {
  140. httpContext.Response.InitResponseWriter();
  141. this.FinishRequest(wr, httpContext, e);
  142. }
  143. }
  144.  
  145. }

HttpRuntime

  1. public sealed class HttpContext : IServiceProvider, IPrincipalContainer
  2. {
  3. //构造函数
  4. public HttpContext(HttpWorkerRequest wr)
  5. {
  6. this._wr = wr;
  7. //初始化HttpContext并创建HttpRequest和HttpResponse
  8. this.Init(new HttpRequest(wr, this), new HttpResponse(wr, this));
  9. //初始化HttpResponse的TextWriter
  10. this._response.InitResponseWriter();
  11. }
  12. private void Init(HttpRequest request, HttpResponse response)
  13. {
  14. this._request = request;
  15. this._response = response;
  16. //省略其他代码
  17. }
  18. }

HttpContext

 3、FileResult

  FileResult用于将某个物理文件的内容响应给客户端!

  1. public abstract class FileResult : ActionResult
  2. {
  3. private string _fileDownloadName;
  4.  
  5. protected FileResult(string contentType)
  6. {
  7. if (String.IsNullOrEmpty(contentType))
  8. {
  9. throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentType");
  10. }
  11.  
  12. ContentType = contentType;
  13. }
  14.  
  15. public string ContentType { get; private set; }
  16.  
  17. public string FileDownloadName
  18. {
  19. get { return _fileDownloadName ?? String.Empty; }
  20. set { _fileDownloadName = value; }
  21. }
  22.  
  23. public override void ExecuteResult(ControllerContext context)
  24. {
  25. if (context == null)
  26. {
  27. throw new ArgumentNullException("context");
  28. }
  29.  
  30. HttpResponseBase response = context.HttpContext.Response;
  31. //response.ContentType默认为“text/html”
  32. response.ContentType = ContentType;
  33. //如果没有指定文件被下载的名称,则按照内联的方法输出文件,否则按照附件的形式。
  34. if (!String.IsNullOrEmpty(FileDownloadName))
  35. {
  36. //处理文件名 并 构造“Content-Disposition”的报头的值
  37. //例如:文件名中包含Unicode码或包含特殊符号等
  38. string headerValue = ContentDispositionUtil.GetHeaderValue(FileDownloadName);
  39. //采用附件形式,需要为响应创建一个名称为“Content-Disposition”的报头,该报头的值格式为“attachment;filename={文件名}”
  40. context.HttpContext.Response.AddHeader("Content-Disposition", headerValue);
  41. }
  42.  
  43. WriteFile(response);
  44. }
  45.  
  46. protected abstract void WriteFile(HttpResponseBase response);
  47.  
  48. //处理文件名并构造 “Content-Disposition”的报头的值
  49. internal static class ContentDispositionUtil
  50. {
  51. private const string HexDigits = "0123456789ABCDEF";
  52.  
  53. private static void AddByteToStringBuilder(byte b, StringBuilder builder)
  54. {
  55. builder.Append('%');
  56.  
  57. int i = b;
  58. AddHexDigitToStringBuilder(i >> 4, builder);
  59. AddHexDigitToStringBuilder(i % 16, builder);
  60. }
  61.  
  62. private static void AddHexDigitToStringBuilder(int digit, StringBuilder builder)
  63. {
  64. builder.Append(HexDigits[digit]);
  65. }
  66.  
  67. private static string CreateRfc2231HeaderValue(string filename)
  68. {
  69. StringBuilder builder = new StringBuilder("attachment; filename*=UTF-8''");
  70.  
  71. byte[] filenameBytes = Encoding.UTF8.GetBytes(filename);
  72. foreach (byte b in filenameBytes)
  73. {
  74. if (IsByteValidHeaderValueCharacter(b))
  75. {
  76. builder.Append((char)b);
  77. }
  78. else
  79. {
  80. AddByteToStringBuilder(b, builder);
  81. }
  82. }
  83.  
  84. return builder.ToString();
  85. }
  86.  
  87. public static string GetHeaderValue(string fileName)
  88. {
  89. // If fileName contains any Unicode characters, encode according
  90. // to RFC 2231 (with clarifications from RFC 5987)
  91. foreach (char c in fileName)
  92. {
  93. if ((int)c > 127)
  94. {
  95. return CreateRfc2231HeaderValue(fileName);
  96. }
  97. }
  98.  
  99. // Knowing there are no Unicode characters in this fileName, rely on
  100. // ContentDisposition.ToString() to encode properly.
  101. // In .Net 4.0, ContentDisposition.ToString() throws FormatException if
  102. // the file name contains Unicode characters.
  103. // In .Net 4.5, ContentDisposition.ToString() no longer throws FormatException
  104. // if it contains Unicode, and it will not encode Unicode as we require here.
  105. // The Unicode test above is identical to the 4.0 FormatException test,
  106. // allowing this helper to give the same results in 4.0 and 4.5.
  107. ContentDisposition disposition = new ContentDisposition() { FileName = fileName };
  108. return disposition.ToString();
  109. }
  110.  
  111. // Application of RFC 2231 Encoding to Hypertext Transfer Protocol (HTTP) Header Fields, sec. 3.2
  112. // http://greenbytes.de/tech/webdav/draft-reschke-rfc2231-in-http-latest.html
  113. private static bool IsByteValidHeaderValueCharacter(byte b)
  114. {
  115. if ((byte)'0' <= b && b <= (byte)'9')
  116. {
  117. return true; // is digit
  118. }
  119. if ((byte)'a' <= b && b <= (byte)'z')
  120. {
  121. return true; // lowercase letter
  122. }
  123. if ((byte)'A' <= b && b <= (byte)'Z')
  124. {
  125. return true; // uppercase letter
  126. }
  127.  
  128. switch (b)
  129. {
  130. case (byte)'-':
  131. case (byte)'.':
  132. case (byte)'_':
  133. case (byte)'~':
  134. case (byte)':':
  135. case (byte)'!':
  136. case (byte)'$':
  137. case (byte)'&':
  138. case (byte)'+':
  139. return true;
  140. }
  141.  
  142. return false;
  143. }
  144. }
  145. }

  对于FileResult,具有一个表示媒体类型的只读属性ContentType,该属性在构造函数中被初始化。当我们基于某个物理文件创建相应的FileReuslt对象的时候应该根据文件的类型指定该媒体类型属性,例如:目标文件是.jpg图片,那么对应的媒体类型应该是“image/jpeg”;对于一个.pdf文件,则采用“application/pdf”。

  对于FileResult,还具有一个表示下载文件名的属性FileDownloadName,如果该属性没有指定或者设置的值为null,则会按照内联的方式利用浏览器直接打开响应的文件,否则会以附件的形式被下载并且文件名为属性FileDownloadName的值。(查看FileResult源码可知,内联和附件的区别是响应是否包含“Content-Disposition”报头)
  FileReult仅仅是一个抽象类,对于文件内容的输出实现在抽象方法WriteFile方法中。FileResult有三个派生类实现了WriterFile方法分别是:

  1. public class FileContentResult : FileResult
  2. {
  3. //参数为字节数组、响应的媒体类型
  4. public FileContentResult(byte[] fileContents, string contentType)
  5. : base(contentType)
  6. {
  7. if (fileContents == null)
  8. {
  9. throw new ArgumentNullException("fileContents");
  10. }
  11.  
  12. FileContents = fileContents;
  13. }
  14.  
  15. public byte[] FileContents { get; private set; }
  16.  
  17. protected override void WriteFile(HttpResponseBase response)
  18. {
  19. //将字节数组输出
  20. response.OutputStream.Write(FileContents, , FileContents.Length);
  21. }
  22. }

FileContentResult

  1. public class FileStreamResult : FileResult
  2. {
  3. // default buffer size as defined in BufferedStream type
  4. private const int BufferSize = 0x1000;
  5. //参数为:文件流、媒体类型
  6. public FileStreamResult(Stream fileStream, string contentType)
  7. : base(contentType)
  8. {
  9. if (fileStream == null)
  10. {
  11. throw new ArgumentNullException("fileStream");
  12. }
  13.  
  14. FileStream = fileStream;
  15. }
  16.  
  17. public Stream FileStream { get; private set; }
  18.  
  19. protected override void WriteFile(HttpResponseBase response)
  20. {
  21. // grab chunks of data and write to the output stream
  22. Stream outputStream = response.OutputStream;
  23. using (FileStream)
  24. {
  25. byte[] buffer = new byte[BufferSize];
  26.  
  27. while (true)
  28. {
  29. int bytesRead = FileStream.Read(buffer, , BufferSize);
  30. if (bytesRead == )
  31. {
  32. // no more data
  33. break;
  34. }
  35. outputStream.Write(buffer, , bytesRead);
  36. }
  37. }
  38. }
  39. }

FileStreamResult

  1. public class FilePathResult : FileResult
  2. {
  3. //参数为:文件路径、媒体类型
  4. public FilePathResult(string fileName, string contentType)
  5. : base(contentType)
  6. {
  7. if (String.IsNullOrEmpty(fileName))
  8. {
  9. throw new ArgumentException(MvcResources.Common_NullOrEmpty, "fileName");
  10. }
  11.  
  12. FileName = fileName;
  13. }
  14.  
  15. public string FileName { get; private set; }
  16.  
  17. protected override void WriteFile(HttpResponseBase response)
  18. {
  19. response.TransmitFile(FileName);
  20. }
  21. }

FilePathResult

  以上的三个继承自FileResult的类,最终都是通过 文件的字节数组 的形式发送到Http输出流,不同的是作为开发者其起始点不一,FileContentResult传入字节数组然后将内容写入当前Http响应的输出流,FileStreamReuslt传入数据流,之后内部存入字节数组再将内容写入当前Http响应的输出流,FilePathResult传入文件地址,之后内部读取文件并存入字节数组再将内容写入当前Http响应的输出流。

在ASP.NET MVC 的Controller类中提供了创建以上三个FileResult派生类的对象的重载,当然也可以直接在Action中创建相应的FileReuslt对象并作为方法的返回值。

  1. public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
  2. {
  3. protected internal FileContentResult File(byte[] fileContents, string contentType)
  4. {
  5. return File(fileContents, contentType, null /* fileDownloadName */);
  6. }
  7.  
  8. protected internal virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName)
  9. {
  10. return new FileContentResult(fileContents, contentType) { FileDownloadName = fileDownloadName };
  11. }
  12.  
  13. protected internal FileStreamResult File(Stream fileStream, string contentType)
  14. {
  15. return File(fileStream, contentType, null /* fileDownloadName */);
  16. }
  17.  
  18. protected internal virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName)
  19. {
  20. return new FileStreamResult(fileStream, contentType) { FileDownloadName = fileDownloadName };
  21. }
  22.  
  23. protected internal FilePathResult File(string fileName, string contentType)
  24. {
  25. return File(fileName, contentType, null /* fileDownloadName */);
  26. }
  27.  
  28. protected internal virtual FilePathResult File(string fileName, string contentType, string fileDownloadName)
  29. {
  30. return new FilePathResult(fileName, contentType) { FileDownloadName = fileDownloadName };
  31. }
  32. }

Controller

 4、JavaScriptResult

  在后台动态的以字符串形式传入一段JavaScript脚本,并作为请求的响应使得脚本在客户端被执行!

  1. public class JavaScriptResult : ActionResult
  2. {
  3. public string Script { get; set; }
  4.  
  5. public override void ExecuteResult(ControllerContext context)
  6. {
  7. if (context == null)
  8. {
  9. throw new ArgumentNullException("context");
  10. }
  11.  
  12. HttpResponseBase response = context.HttpContext.Response;
  13. //指定响应的媒体类型
  14. response.ContentType = "application/x-javascript";
  15.  
  16. if (Script != null)
  17. {
  18. response.Write(Script);
  19. }
  20. }
  21. }

  通过JavaScriptResult源码可以看出,其输出方式和ContentResult相同,不同的只是在JavaScriptResult中内部指定了输出的媒体类型为“application/x-javascript”(也可以是“text/javascript”),而我们也可以通过设置ContentResult的输出媒体类型来实现与JavaScriptResult相同的功能!

  在ASP.NET MVC 的Controller类中提供了创建JavaScriptResult对象的方法,当然也可以直接在Action中创建JavaScriptResult对象并作为方法的返回值。

  1. public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
  2. {
  3. //省略其他代码
  4. protected internal virtual JavaScriptResult JavaScript(string script)
  5. {
  6. return new JavaScriptResult { Script = script };
  7. }
  8. }

Controller

5、JsonResult

  JsonResutl用于以Json的格式返回响应的数据!

  1. public class JsonResult : ActionResult
  2. {
  3. public JsonResult()
  4. {
  5. //定义枚举类型,默认拒绝Get请求的响应
  6. JsonRequestBehavior = JsonRequestBehavior.DenyGet;
  7. }
  8.  
  9. public Encoding ContentEncoding { get; set; }
  10.  
  11. public string ContentType { get; set; }
  12.  
  13. public object Data { get; set; }
  14.  
  15. //是否决绝Http Get请求(默认拒绝---构造函数中定义)
  16. public JsonRequestBehavior JsonRequestBehavior { get; set; }
  17.  
  18. /// <summary>
  19. ///指定 JSON 字符串的最大长度(UTF-8 字符的最大数量)。 默认长度为 102400。
  20. /// </summary>
  21. public int? MaxJsonLength { get; set; }
  22.  
  23. /// <summary>
  24. /// 指定要序列化类型的最大深度。 默认的递归限制为 100。
  25. /// </summary>
  26. public int? RecursionLimit { get; set; }
  27.  
  28. public override void ExecuteResult(ControllerContext context)
  29. {
  30. if (context == null)
  31. {
  32. throw new ArgumentNullException("context");
  33. }
  34. //如果拒绝Get请求&&发送来的请求也是Get方式
  35. if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
  36. String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
  37. {
  38. throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);
  39. }
  40.  
  41. HttpResponseBase response = context.HttpContext.Response;
  42.  
  43. //默认媒体类型为"application/json"
  44. if (!String.IsNullOrEmpty(ContentType))
  45. {
  46. response.ContentType = ContentType;
  47. }
  48. else
  49. {
  50. response.ContentType = "application/json";
  51. }
  52. //编码类型的选取还是和ContentResult中一样,优先级:显示设定>WebConfig中节点>Encoding.Default
  53. if (ContentEncoding != null)
  54. {
  55. response.ContentEncoding = ContentEncoding;
  56. }
  57. if (Data != null)
  58. {
  59. //通过JavaScriptSerializer来将CLR对象序列化成Json格式字符串
  60. JavaScriptSerializer serializer = new JavaScriptSerializer();
  61. if (MaxJsonLength.HasValue)
  62. {
  63. //serializer.MaxJsonLength是JSON 字符串的最大长度(UTF-8 字符的最大数量)。 默认长度为 102400
  64. serializer.MaxJsonLength = MaxJsonLength.Value;
  65. }
  66. if (RecursionLimit.HasValue)
  67. {
  68. //serializer.RecursionLimit是指要序列化类型的最大深度。 默认的递归限制为 100
  69. serializer.RecursionLimit = RecursionLimit.Value;
  70. }
  71. //将Json格式的字符串写入当前Http响应的输出流
  72. response.Write(serializer.Serialize(Data));
  73. }
  74. }
  75. }
  1. public enum JsonRequestBehavior
  2. {
  3. AllowGet,
  4. DenyGet,
  5. }

  对于JsonResult,其构造函数中为属性JsonRequestBehavior设置了一个枚举值DenyGet,该枚举值的作用就是拒绝对GET请求进行响应,也就是默认情况下,对于Json格式的数据响应,Get请求是不予支持的。如果想要支持Get请求,可以显示的设置JsonRequestBehavior属性的枚举值为AllowGet。
  对于JsonResult,其默认的媒体类型为“application/json”。
  JsonResult就是将CLR对象到Json格式字符串的序列化过程,而上述源码中的object类型的Data属性就是用来获取或设置原始的CLR对象,原始的CLR对象通过JavaScriptSerializer类的Serialize方法的序列化,将CLR对象转换成Json格式的字符串。在JavaScriptSerializer类在对CLR对象进行序列化时还可以对过程进行一些设置,即:MaxJsonLength(Json字符串的最大长度)、RecursionLimit(序列化类时递归的最大深度)。可以在JsonResult对应的属性中设置,也可以在WebConfig中设置。更多设置

  1. <configuration>
  2. <system.web.extensions>
  3. <scripting>
  4. <webServices>
  5. <jsonSerialization maxJsonLength="5000"/>
  6. </webServices>
  7. </scripting>
  8. </system.web.extensions>
  9. </configuration>

  在ASP.NET MVC 的Controller类中提供了一下创建JsonResult对象的方法,当然也可以直接在Action中创建JsonResult对象并作为方法的返回值。

  1. public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
  2. {
  3. //省略其他代码
  4. protected internal JsonResult Json(object data)
  5. {
  6. return Json(data, null /* contentType */, null /* contentEncoding */, JsonRequestBehavior.DenyGet);
  7. }
  8.  
  9. protected internal JsonResult Json(object data, string contentType)
  10. {
  11. return Json(data, contentType, null /* contentEncoding */, JsonRequestBehavior.DenyGet);
  12. }
  13.  
  14. protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding)
  15. {
  16. return Json(data, contentType, contentEncoding, JsonRequestBehavior.DenyGet);
  17. }
  18.  
  19. protected internal JsonResult Json(object data, JsonRequestBehavior behavior)
  20. {
  21. return Json(data, null /* contentType */, null /* contentEncoding */, behavior);
  22. }
  23.  
  24. protected internal JsonResult Json(object data, string contentType, JsonRequestBehavior behavior)
  25. {
  26. return Json(data, contentType, null /* contentEncoding */, behavior);
  27. }
  28.  
  29. protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)
  30. {
  31. return new JsonResult
  32. {
  33. Data = data,
  34. ContentType = contentType,
  35. ContentEncoding = contentEncoding,
  36. JsonRequestBehavior = behavior
  37. };
  38. }
  39. }

Controller

6、HttpStatusCodeResult

  HttpStatusCodeResult用于返回对Http请求响应状态的代码和一个可选的状态描述!

  1. public class HttpStatusCodeResult : ActionResult
  2. {
  3. public HttpStatusCodeResult(int statusCode)
  4. : this(statusCode, null)
  5. {
  6. }
  7. //HttStatusCode是个枚举类型,用于定义状态代码
  8. public HttpStatusCodeResult(HttpStatusCode statusCode)
  9. : this(statusCode, null)
  10. {
  11. }
  12.  
  13. public HttpStatusCodeResult(HttpStatusCode statusCode, string statusDescription)
  14. : this((int)statusCode, statusDescription)
  15. {
  16. }
  17.  
  18. public HttpStatusCodeResult(int statusCode, string statusDescription)
  19. {
  20. StatusCode = statusCode;
  21. StatusDescription = statusDescription;
  22. }
  23. //响应状态代码
  24. public int StatusCode { get; private set; }
  25. //响应状态描述
  26. public string StatusDescription { get; private set; }
  27.  
  28. public override void ExecuteResult(ControllerContext context)
  29. {
  30. if (context == null)
  31. {
  32. throw new ArgumentNullException("context");
  33. }
  34. //默认状态代码为:200
  35. context.HttpContext.Response.StatusCode = StatusCode;
  36. if (StatusDescription != null)
  37. {
  38. context.HttpContext.Response.StatusDescription = StatusDescription;
  39. }
  40. }
  41. }

  HttpStatusCodeResult为Http的响应头设置状态代码和状态描述,设置时,可以通过构造函数传入值也可以通过给属性赋值来操作。对于HttpStatustCodeResult的构造函数中HttpStatusCode类型的参数,它是一个枚举类型,其中包含了众多Http响应头状态。
  值得一说的是,如果我们采用Visual StudioDvelopment Server作为Web应用的宿主,通过HttpStatusCodeResult的StatusDescription属性设置的状态描述信息不会反映在Http响应中,只有采用IIS作为宿主才会真正将此信息写入响应消息。

  1. public enum HttpStatusCode
  2. {
  3. Continue = ,
  4. SwitchingProtocols,
  5. OK = ,
  6. Created,
  7. Accepted,
  8. NonAuthoritativeInformation,
  9. NoContent,
  10. ResetContent,
  11. PartialContent,
  12. MultipleChoices = ,
  13. Ambiguous = ,
  14. MovedPermanently,
  15. Moved = ,
  16. Found,
  17. Redirect = ,
  18. SeeOther,
  19. RedirectMethod = ,
  20. NotModified,
  21. UseProxy,
  22. Unused,
  23. TemporaryRedirect,
  24. RedirectKeepVerb = ,
  25. BadRequest = ,
  26. Unauthorized,
  27. PaymentRequired,
  28. Forbidden,
  29. NotFound,
  30. MethodNotAllowed,
  31. NotAcceptable,
  32. ProxyAuthenticationRequired,
  33. RequestTimeout,
  34. Conflict,
  35. Gone,
  36. LengthRequired,
  37. PreconditionFailed,
  38. RequestEntityTooLarge,
  39. RequestUriTooLong,
  40. UnsupportedMediaType,
  41. RequestedRangeNotSatisfiable,
  42. ExpectationFailed,
  43. UpgradeRequired = ,
  44. InternalServerError = ,
  45. NotImplemented,
  46. BadGateway,
  47. ServiceUnavailable,
  48. GatewayTimeout,
  49. HttpVersionNotSupported
  50. }

HttpStatusCode

  ASP.NET MVC中有两个继承自HttpStatusCodeResult的类,即:HttpNotFoundResult和AuthorizeAttribute,用于指定特定相应状态和状态描述,本质上还是执行HttpStatusCodeResult来完成,只不过在内部为HttpStatuCodeResult指定了响应状态,分别是404、401。

  1. public class HttpNotFoundResult : HttpStatusCodeResult
  2. {
  3. public HttpNotFoundResult()
  4. : this(null)
  5. {
  6. }
  7.  
  8. // NotFound is equivalent to HTTP status 404.
  9. public HttpNotFoundResult(string statusDescription)
  10. : base(HttpStatusCode.NotFound, statusDescription)
  11. {
  12. }
  13. }

HttpNotFoundResult

  1. public class HttpUnauthorizedResult : HttpStatusCodeResult
  2. {
  3. public HttpUnauthorizedResult()
  4. : this(null)
  5. {
  6. }
  7.  
  8. // Unauthorized is equivalent to HTTP status 401, the status code for unauthorized
  9. // access. Other code might intercept this and perform some special logic. For
  10. // example, the FormsAuthenticationModule looks for 401 responses and instead
  11. // redirects the user to the login page.
  12. public HttpUnauthorizedResult(string statusDescription)
  13. : base(HttpStatusCode.Unauthorized, statusDescription)
  14. {
  15. }
  16. }

HttpUnauthorizedResult

7、RedirecteResult

  RedirectResult用于实现针对某个地址的重定向!

  1. public class RedirectResult : ActionResult
  2. {
  3. public RedirectResult(string url)
  4. : this(url, permanent: false)
  5. {
  6. }
  7.  
  8. public RedirectResult(string url, bool permanent)
  9. {
  10. if (String.IsNullOrEmpty(url))
  11. {
  12. throw new ArgumentException(MvcResources.Common_NullOrEmpty, "url");
  13. }
  14.  
  15. Permanent = permanent;
  16. Url = url;
  17. }
  18. //是否永久重定向,默认为否。(永久重定向的Http状态码为301,否则是暂时重定向Http状态码为302)
  19. public bool Permanent { get; private set; }
  20. //要跳转的地址(相对地址或绝对地址)
  21. public string Url { get; private set; }
  22.  
  23. public override void ExecuteResult(ControllerContext context)
  24. {
  25. if (context == null)
  26. {
  27. throw new ArgumentNullException("context");
  28. }
  29. if (context.IsChildAction)
  30. {
  31. throw new InvalidOperationException(MvcResources.RedirectAction_CannotRedirectInChildAction);
  32. }
  33. //处理Url地址,相对地址的处理。
  34. string destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
  35. context.Controller.TempData.Keep();
  36. //是否永久重定向
  37. if (Permanent)
  38. {
  39. context.HttpContext.Response.RedirectPermanent(destinationUrl, endResponse: false);
  40. }
  41. else
  42. {
  43. context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
  44. }
  45. }
  46. }

  对于RedirectResult,可以定义暂时重定向(302重定向)和永久重定向(301重定向),两种重定向的不同作用主要体现在SEO上,搜索引擎会使用永久重定向目标地址更新自己的索引,而暂时重定向则不会。另外,永久重定向是在ASP.NET 4之后引进的,在之前如果想要实现永久重定向的话,需要自己来设置Http响应状态码为301。
  对于UrlHelper.GenerateCotentUrl方法,用来处理Url。当定义的Url为相对地址时,如:~/xxx/xxx,该方法会利用请求上下文来补全地址。

  1. public static string GenerateContentUrl(string contentPath, HttpContextBase httpContext)
  2. {
  3. if (string.IsNullOrEmpty(contentPath))
  4. {
  5. throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentPath");
  6. }
  7. if (httpContext == null)
  8. {
  9. throw new ArgumentNullException("httpContext");
  10. }
  11. if (contentPath[0] == '~')
  12. {
  13. return PathHelpers.GenerateClientUrl(httpContext, contentPath);
  14. }
  15. return contentPath;
  16. }

  对于ASP.NET MVC的Controller类中定义了一下几个方法来创建RedirectResult,然也可以直接在Action中创建RedirectResult对象并作为方法的返回值。

  1. public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
  2. {
  3. //省略其他代码
  4. protected internal virtual RedirectResult Redirect(string url)
  5. {
  6. if (String.IsNullOrEmpty(url))
  7. {
  8. throw new ArgumentException(MvcResources.Common_NullOrEmpty, "url");
  9. }
  10.  
  11. return new RedirectResult(url);
  12. }
  13.  
  14. protected internal virtual RedirectResult RedirectPermanent(string url)
  15. {
  16. if (String.IsNullOrEmpty(url))
  17. {
  18. throw new ArgumentException(MvcResources.Common_NullOrEmpty, "url");
  19. }
  20.  
  21. return new RedirectResult(url, permanent: true);
  22. }

Controller

8、RedirectToRoutResult

  RedirectToRouteResult用于将路由信息中的Controller和Action拼接成Url,再进行跳转!

  1. public class RedirectToRouteResult : ActionResult
  2. {
  3. private RouteCollection _routes;
  4.  
  5. public RedirectToRouteResult(RouteValueDictionary routeValues)
  6. :
  7. this(null, routeValues)
  8. {
  9. }
  10.  
  11. public RedirectToRouteResult(string routeName, RouteValueDictionary routeValues)
  12. : this(routeName, routeValues, permanent: false)
  13. {
  14. }
  15.  
  16. public RedirectToRouteResult(string routeName, RouteValueDictionary routeValues, bool permanent)
  17. {
  18. Permanent = permanent;
  19. RouteName = routeName ?? String.Empty;
  20. RouteValues = routeValues ?? new RouteValueDictionary();
  21. }
  22.  
  23. public bool Permanent { get; private set; }
  24.  
  25. public string RouteName { get; private set; }
  26.  
  27. public RouteValueDictionary RouteValues { get; private set; }
  28.  
  29. internal RouteCollection Routes
  30. {
  31. get
  32. {
  33. if (_routes == null)
  34. {
  35. _routes = RouteTable.Routes;
  36. }
  37. return _routes;
  38. }
  39. set { _routes = value; }
  40. }
  41.  
  42. public override void ExecuteResult(ControllerContext context)
  43. {
  44. if (context == null)
  45. {
  46. throw new ArgumentNullException("context");
  47. }
  48. if (context.IsChildAction)
  49. {
  50. throw new InvalidOperationException(MvcResources.RedirectAction_CannotRedirectInChildAction);
  51. }
  52.  
  53. string destinationUrl = UrlHelper.GenerateUrl(RouteName, null /* actionName */, null /* controllerName */, RouteValues, Routes, context.RequestContext, false /* includeImplicitMvcValues */);
  54. if (String.IsNullOrEmpty(destinationUrl))
  55. {
  56. throw new InvalidOperationException(MvcResources.Common_NoRouteMatched);
  57. }
  58.  
  59. context.Controller.TempData.Keep();
  60.  
  61. if (Permanent)
  62. {
  63. context.HttpContext.Response.RedirectPermanent(destinationUrl, endResponse: false);
  64. }
  65. else
  66. {
  67. context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
  68. }
  69. }
  70. }

  RedirectToRouteResult和RedirectResult都是实现重定向,只不过RedirectToRouteResult的跳转地址是通过路由信息中的Controller和Action的拼接来完成的,其他均和RedirectResult相同!

  ASP.NET MVC在Controller类中定义了几个方法用于创建RedirectToRouteResult对象,当然也可以直接在Action中创建RedirectToRouteResult对象并作为方法的返回值。

  1. public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
  2. {
  3. //省略其他代码
  4. protected internal RedirectToRouteResult RedirectToAction(string actionName)
  5. {
  6. return RedirectToAction(actionName, (RouteValueDictionary)null);
  7. }
  8.  
  9. protected internal RedirectToRouteResult RedirectToAction(string actionName, object routeValues)
  10. {
  11. return RedirectToAction(actionName, new RouteValueDictionary(routeValues));
  12. }
  13.  
  14. protected internal RedirectToRouteResult RedirectToAction(string actionName, RouteValueDictionary routeValues)
  15. {
  16. return RedirectToAction(actionName, null /* controllerName */, routeValues);
  17. }
  18.  
  19. protected internal RedirectToRouteResult RedirectToAction(string actionName, string controllerName)
  20. {
  21. return RedirectToAction(actionName, controllerName, (RouteValueDictionary)null);
  22. }
  23.  
  24. protected internal RedirectToRouteResult RedirectToAction(string actionName, string controllerName, object routeValues)
  25. {
  26. return RedirectToAction(actionName, controllerName, new RouteValueDictionary(routeValues));
  27. }
  28.  
  29. protected internal virtual RedirectToRouteResult RedirectToAction(string actionName, string controllerName, RouteValueDictionary routeValues)
  30. {
  31. RouteValueDictionary mergedRouteValues;
  32.  
  33. if (RouteData == null)
  34. {
  35. mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, null, routeValues, includeImplicitMvcValues: true);
  36. }
  37. else
  38. {
  39. mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, RouteData.Values, routeValues, includeImplicitMvcValues: true);
  40. }
  41.  
  42. return new RedirectToRouteResult(mergedRouteValues);
  43. }
  44.  
  45. protected internal RedirectToRouteResult RedirectToActionPermanent(string actionName)
  46. {
  47. return RedirectToActionPermanent(actionName, (RouteValueDictionary)null);
  48. }
  49.  
  50. protected internal RedirectToRouteResult RedirectToActionPermanent(string actionName, object routeValues)
  51. {
  52. return RedirectToActionPermanent(actionName, new RouteValueDictionary(routeValues));
  53. }
  54.  
  55. protected internal RedirectToRouteResult RedirectToActionPermanent(string actionName, RouteValueDictionary routeValues)
  56. {
  57. return RedirectToActionPermanent(actionName, null /* controllerName */, routeValues);
  58. }
  59.  
  60. protected internal RedirectToRouteResult RedirectToActionPermanent(string actionName, string controllerName)
  61. {
  62. return RedirectToActionPermanent(actionName, controllerName, (RouteValueDictionary)null);
  63. }
  64.  
  65. protected internal RedirectToRouteResult RedirectToActionPermanent(string actionName, string controllerName, object routeValues)
  66. {
  67. return RedirectToActionPermanent(actionName, controllerName, new RouteValueDictionary(routeValues));
  68. }
  69.  
  70. protected internal virtual RedirectToRouteResult RedirectToActionPermanent(string actionName, string controllerName, RouteValueDictionary routeValues)
  71. {
  72. RouteValueDictionary implicitRouteValues = (RouteData != null) ? RouteData.Values : null;
  73.  
  74. RouteValueDictionary mergedRouteValues =
  75. RouteValuesHelpers.MergeRouteValues(actionName, controllerName, implicitRouteValues, routeValues, includeImplicitMvcValues: true);
  76.  
  77. return new RedirectToRouteResult(null, mergedRouteValues, permanent: true);
  78. }
  79.  
  80. protected internal RedirectToRouteResult RedirectToRoute(object routeValues)
  81. {
  82. return RedirectToRoute(new RouteValueDictionary(routeValues));
  83. }
  84.  
  85. protected internal RedirectToRouteResult RedirectToRoute(RouteValueDictionary routeValues)
  86. {
  87. return RedirectToRoute(null /* routeName */, routeValues);
  88. }
  89.  
  90. protected internal RedirectToRouteResult RedirectToRoute(string routeName)
  91. {
  92. return RedirectToRoute(routeName, (RouteValueDictionary)null);
  93. }
  94.  
  95. protected internal RedirectToRouteResult RedirectToRoute(string routeName, object routeValues)
  96. {
  97. return RedirectToRoute(routeName, new RouteValueDictionary(routeValues));
  98. }
  99.  
  100. protected internal virtual RedirectToRouteResult RedirectToRoute(string routeName, RouteValueDictionary routeValues)
  101. {
  102. return new RedirectToRouteResult(routeName, RouteValuesHelpers.GetRouteValues(routeValues));
  103. }
  104.  
  105. protected internal RedirectToRouteResult RedirectToRoutePermanent(object routeValues)
  106. {
  107. return RedirectToRoutePermanent(new RouteValueDictionary(routeValues));
  108. }
  109.  
  110. protected internal RedirectToRouteResult RedirectToRoutePermanent(RouteValueDictionary routeValues)
  111. {
  112. return RedirectToRoutePermanent(null /* routeName */, routeValues);
  113. }
  114.  
  115. protected internal RedirectToRouteResult RedirectToRoutePermanent(string routeName)
  116. {
  117. return RedirectToRoutePermanent(routeName, (RouteValueDictionary)null);
  118. }
  119.  
  120. protected internal RedirectToRouteResult RedirectToRoutePermanent(string routeName, object routeValues)
  121. {
  122. return RedirectToRoutePermanent(routeName, new RouteValueDictionary(routeValues));
  123. }
  124.  
  125. protected internal virtual RedirectToRouteResult RedirectToRoutePermanent(string routeName, RouteValueDictionary routeValues)
  126. {
  127. return new RedirectToRouteResult(routeName, RouteValuesHelpers.GetRouteValues(routeValues), permanent: true);
  128. }
  129.  

Controller

9、ViewResult

  ViewResult内容包含了:PartialViewResult和ViewResult。ViewResult将视图页的内容响应给客户端,而PartialViewResult称分部视图,其响应请求时不输出那写html、head、body等标签,只是将分部视图中内容返回!由于ViewResult和PartialViewResult在进行【View呈现】的过程大致相同,所以此处就只针对ViewResult进行详细解读,而PartialViewRsult详细过程将不再敖述。(分部视图的更多信息:关于如何PartialViewResult的使用)

  1. public abstract class ViewResultBase : ActionResult
  2. {
  3. private DynamicViewDataDictionary _dynamicViewData;
  4. private TempDataDictionary _tempData;
  5. private ViewDataDictionary _viewData;
  6. private ViewEngineCollection _viewEngineCollection;
  7. private string _viewName;
  8.  
  9. public object Model
  10. {
  11. get { return ViewData.Model; }
  12. }
  13.  
  14. public TempDataDictionary TempData
  15. {
  16. get
  17. {
  18. if (_tempData == null)
  19. {
  20. _tempData = new TempDataDictionary();
  21. }
  22. return _tempData;
  23. }
  24. set { _tempData = value; }
  25. }
  26.  
  27. public IView View { get; set; }
  28.  
  29. public dynamic ViewBag
  30. {
  31. get
  32. {
  33. if (_dynamicViewData == null)
  34. {
  35. _dynamicViewData = new DynamicViewDataDictionary(() => ViewData);
  36. }
  37. return _dynamicViewData;
  38. }
  39. }
  40. public ViewDataDictionary ViewData
  41. {
  42. get
  43. {
  44. if (_viewData == null)
  45. {
  46. _viewData = new ViewDataDictionary();
  47. }
  48. return _viewData;
  49. }
  50. set { _viewData = value; }
  51. }
  52.  
  53. //获取或设置视图引擎,ASP.NET有两个视图引擎,分别是:WebFormViewEngine、RazorViewEngine。
  54. public ViewEngineCollection ViewEngineCollection
  55. {
  56. get { return _viewEngineCollection ?? ViewEngines.Engines; }
  57. set { _viewEngineCollection = value; }
  58. }
  59.  
  60. public string ViewName
  61. {
  62. get { return _viewName ?? String.Empty; }
  63. set { _viewName = value; }
  64. }
  65.  
  66. public override void ExecuteResult(ControllerContext context)
  67. {
  68. if (context == null)
  69. {
  70. throw new ArgumentNullException("context");
  71. }
  72. //如果没有设置ViewName就将当前Action作为ViewName
  73. if (String.IsNullOrEmpty(ViewName))
  74. {
  75. ViewName = context.RouteData.GetRequiredString("action");
  76. }
  77.  
  78. ViewEngineResult result = null;
  79.  
  80. if (View == null)
  81. {
  82. //通过视图引擎去寻找视图
  83. result = FindView(context);
  84. View = result.View;
  85. }
  86.  
  87. TextWriter writer = context.HttpContext.Response.Output;
  88. ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);
  89. //使用指定的编写器对象来呈现指定的视图上下文
  90. View.Render(viewContext, writer);
  91.  
  92. if (result != null)
  93. {
  94. result.ViewEngine.ReleaseView(context, View);
  95. }
  96. }
  97.  
  98. protected abstract ViewEngineResult FindView(ControllerContext context);
  99. }

ViewResultBase

  1. public class ViewResult : ViewResultBase
  2. {
  3. private string _masterName;
  4.  
  5. public string MasterName
  6. {
  7. get { return _masterName ?? String.Empty; }
  8. set { _masterName = value; }
  9. }
  10.  
  11. protected override ViewEngineResult FindView(ControllerContext context)
  12. {
  13. //根据View引擎去寻找View
  14. //此处ViewEngineCollection是ViewResultBase类中的一个属性,表示视图引擎集合。
  15. ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);
  16. //如果找到了指定的VIew,则返回。
  17. if (result.View != null)
  18. {
  19. return result;
  20. }
  21. //没有找到指定的View,那么就将查找路径给通过异常返回。
  22. StringBuilder locationsText = new StringBuilder();
  23. foreach (string location in result.SearchedLocations)
  24. {
  25. locationsText.AppendLine();
  26. locationsText.Append(location);
  27. }
  28. throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
  29. MvcResources.Common_ViewNotFound, ViewName, locationsText));
  30. }
  31. }

ViewResult

  1. public class PartialViewResult : ViewResultBase
  2. {
  3. /// <summary>Returns the <see cref="T:System.Web.Mvc.ViewEngineResult" /> object that is used to render the view.</summary>
  4. /// <returns>The view engine result.</returns>
  5. /// <param name="context">The controller context.</param>
  6. /// <exception cref="T:System.InvalidOperationException">An error occurred while the method was attempting to find the view.</exception>
  7. protected override ViewEngineResult FindView(ControllerContext context)
  8. {
  9. ViewEngineResult viewEngineResult = base.ViewEngineCollection.FindPartialView(context, base.ViewName);
  10. if (viewEngineResult.View != null)
  11. {
  12. return viewEngineResult;
  13. }
  14. StringBuilder stringBuilder = new StringBuilder();
  15. foreach (string current in viewEngineResult.SearchedLocations)
  16. {
  17. stringBuilder.AppendLine();
  18. stringBuilder.Append(current);
  19. }
  20. throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.Common_PartialViewNotFound, new object[]
  21. {
  22. base.ViewName,
  23. stringBuilder
  24. }));
  25. }
  26. }

PartialViewResult

Controller类中定义的创建ViewResult和PartialViewResult对象的方法:

  1. public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer
  2. {
  3. //省略其他代码...
  4.  
  5. //PartialViewResult
  6. protected internal PartialViewResult PartialView()
  7. {
  8. return this.PartialView(null, null);
  9. }
  10. protected internal PartialViewResult PartialView(object model)
  11. {
  12. return this.PartialView(null, model);
  13. }
  14. protected internal PartialViewResult PartialView(string viewName)
  15. {
  16. return this.PartialView(viewName, null);
  17. }
  18. protected internal virtual PartialViewResult PartialView(string viewName, object model)
  19. {
  20. if (model != null)
  21. {
  22. base.ViewData.Model = model;
  23. }
  24. return new PartialViewResult
  25. {
  26. ViewName = viewName,
  27. ViewData = base.ViewData,
  28. TempData = base.TempData,
  29. ViewEngineCollection = this.ViewEngineCollection
  30. };
  31. }
  32. //ViewResult
  33. protected internal ViewResult View()
  34. {
  35. string viewName = null;
  36. string masterName = null;
  37. object model = null;
  38. return this.View(viewName, masterName, model);
  39. }
  40. protected internal ViewResult View(object model)
  41. {
  42. return this.View(null, null, model);
  43. }
  44. protected internal ViewResult View(string viewName)
  45. {
  46. string masterName = null;
  47. object model = null;
  48. return this.View(viewName, masterName, model);
  49. }
  50. protected internal ViewResult View(string viewName, string masterName)
  51. {
  52. return this.View(viewName, masterName, null);
  53. }
  54. protected internal ViewResult View(string viewName, object model)
  55. {
  56. return this.View(viewName, null, model);
  57. }
  58. protected internal virtual ViewResult View(string viewName, string masterName, object model)
  59. {
  60. if (model != null)
  61. {
  62. base.ViewData.Model = model;
  63. }
  64. return new ViewResult
  65. {
  66. ViewName = viewName,
  67. MasterName = masterName,
  68. ViewData = base.ViewData,
  69. TempData = base.TempData,
  70. ViewEngineCollection = this.ViewEngineCollection
  71. };
  72. }
  73. protected internal ViewResult View(IView view)
  74. {
  75. return this.View(view, null);
  76. }
  77. protected internal virtual ViewResult View(IView view, object model)
  78. {
  79. if (model != null)
  80. {
  81. base.ViewData.Model = model;
  82. }
  83. return new ViewResult
  84. {
  85. View = view,
  86. ViewData = base.ViewData,
  87. TempData = base.TempData
  88. };
  89. }
  90. }

Controller

ViewResult进行呈现的大致流程为:

  • 获取视图引擎,默认有两个:ASPX引擎、Razor引擎。
  • 根据视图页名称,通过视图引擎去检查是否存在对应的视图页,如果存在,则创建视图对象。如果不存在,则将所有视图引擎寻找过的路径作为异常返回。
  • 创建视图对象之后,处理视图页中的内容(先处理_ViewStart.cshtml,之后再处理相应的试图页)。例如:TempData、Html.XXX等。
  • 视图页内容处理完毕之后,就将视图内容作为响应返回给客户端。

  对于上述流程中的第三步中,创建视图对象之后,通过它来对视图页进行处理。在对处理视图页时,首先要处理_ViewStart.cshtml文件(相当与asp.net中的Page_Load方法),之后再去处理请求的试图页。例如:如果在~/View/HomeController目录下创建一个_ViewStart.cshtml文件,那么之后当请求HomeController目录下的任意视图页时,都会先执行_ViewStart.cshtml,如果再在~/View目录下创建一个_ViewStart.cshtml的话,那么在请求HomeController目录下的任意视图页时,那么两个_ViewStart.cshtml都会先执行,且顺序为:先~/View目录下后~/View/HomeController目录下的_ViewStart.cshtml。

由于ViewResult的详细过程涉及内容较多,所以将另写一篇博文来对其进行详细分析:《白话学习MVC(十)View的呈现二》

白话学习MVC(九)View的呈现一的更多相关文章

  1. 白话学习MVC(十)View的呈现二

    本节将接着<白话学习MVC(九)View的呈现一>来继续对ViewResult的详细执行过程进行分析! 9.ViewResult ViewResult将视图页的内容响应给客户端! 由于Vi ...

  2. 白话学习MVC(八)Action的执行二

    一.概述 上篇博文<白话学习MVC(七)Action的执行一>介绍了ASP.NET MVC中Action的执行的简要流程,并且对TempData的运行机制进行了详细的分析,本篇来分析上一篇 ...

  3. 白话学习MVC(七)Action的执行一

    一.概述 在此系列开篇的时候介绍了MVC的生命周期 , 对于请求的处理,都是将相应的类的方法注册到HttpApplication事件中,通过事件的依次执行从而完成对请求的处理.对于MVC来说,请求是先 ...

  4. Asp.net mvc 中View 的呈现(二)

    [toc] 上一节介绍了 Asp.net mvc 中除 ViewResult 外的所有的 ActionResult,这一节介绍 ViewResult. ViewResultBase ViewResul ...

  5. 白话学习MVC(六)模型绑定

    一.什么是模型绑定? 模型绑定存在的意义就是为Action的参数提供值,例如:如下表单中提交了数据,那么Action(即:Index)的参数Id,Name的值就是表单中对应的name属性相同的值,而表 ...

  6. 白话学习MVC(五)Controller的激活

    一.概述 在此系列开篇的时候介绍了MVC的生命周期 , 对于请求的处理,都是将相应的类的方法注册到HttpApplication事件中,通过事件的依次执行从而完成对请求的处理.对于MVC来说,请求是先 ...

  7. Asp.net mvc 中View的呈现(一)

    [toc] 我们知道针对客户端的请求,最终都会转换为对 Controller 中的一个 Action 方法的调用,指定的 Action 方法会返回一个 ActionResult 类型的实例来响应该请求 ...

  8. Artech的MVC4框架学习——第八章View的呈现

    总结:定义在controller中的action方法一般会返回actionResult的对象对请求给予 响应.viewResult是最常见也是最重要的ActionView的一种(p411).view模 ...

  9. 学习ASP.NET MVC(九)——“Code First Migrations ”工具使用示例

    在上一篇文章中,我们学习了如何使用实体框架的“Code First Migrations ”工具,使用其中的“迁移”功能对模型类进行一些修改,同时同步更新对应数据库的表结构. 在本文章中,我们将使用“ ...

随机推荐

  1. Hadoop核心组件

    1.Hadoop生态系统 2.HDFS(Hadoop分布式文件系统) 源自于Google的GFS论文,发表于2003年10月,HDFS是GFS克隆版. 是Hadoop体系中数据存储管理的基础.它是一个 ...

  2. ORACLE11g JDBC Driver

    http://blog.163.com/z_rx/blog/static/276363762011312947507/ ORACLE服务器端安装程序找到相应目录"x$\app\Adminis ...

  3. CSS3 transform rotate(旋转)锯齿的解决办法

    -moz-transform: rotate(5deg);-webkit-transform: rotate(5deg); 把图片旋转了5度.本以为轻而易举,可遇到了问题.在Fireofx中显示正常, ...

  4. js的BOM对象完全解析

    BOM即浏览器对象模型,它包括如下一些对象! (一)screen对象,Screen 对象中存放着有关显示浏览器屏幕的信息. 常见的属性有: availHeight:返回显示屏幕的高度 availWid ...

  5. [友盟微博分享]does not contain bitcode. You must rebuild it with

    1.我的 Xcode 明明打开了 bitcode 了,为什么还会报错呢?挺奇怪的. 2.上网一查,才知道,友盟需要 bitcode,Xcode 不支持,只要关闭bitcode就可以 了. 3.其实我也 ...

  6. Maven with Multi-module

    Well, A project made of multi modules. For example, the hongten-security project, the structure of t ...

  7. vim 使用笔记

    vim命令笔记 a 插入 insert 插入 :%!xxd 以16进制方式进行编辑 :%!xxd -r 从16进制还原

  8. osgearth各个例子功能概述

    osgearth各个例子功能概述 转自:http://blog.csdn.net/wl198302/article/details/21177309 最近在学习osgearth,对其还不是很理解,有些 ...

  9. Emmet:HTML/CSS代码快速编写神器

    本文来源:http://www.iteye.com/news/27580    ,还可参考:http://www.w3cplus.com/tools/emmet-cheat-sheet.html Em ...

  10. selenium grid中的多个线程同步执行

    需求:有一个工作流,每一步审批都需要多个领导参与,才能推流程到下一步去 代码思考:多个领导在自己的线程中运行,速度有的快有的慢,如何保证下一步的领导审批时,这个步骤已经激活 如下是代码:思路为:如果这 ...