白話學習MVC(九)View的呈現一

1、概述

  本節來看一下ASP.NET MVC【View的呈現】的內容,View的呈現是在Action執行以後進行,Action的執行生成一個ActionResult,【View的呈現】的功能就是:經過InvokeActionResult方法對【Action的執行】中生成的ActionResult進行處理。(ActionResult泛指那些繼承自抽象類System.Web.Mvc.ActonResult的類的實例)javascript

  爲了會縱觀【View的呈現】在全局中的位置,下面咱們再來回顧下處理請求的整個流程:在此係列開篇的時候介紹了MVC的生命週期 , 對於ASP.NET和ASP.NET MVC,都是將相應的類的方法註冊到HttpApplication事件中,經過事件的依次執行從而完成對請求的處理。而針對MVC,請求是先 通過路由系統,而後由一個MvcHandler來處理的,當請求到來時,執行此MvcHandler的ProcessRequest方法(由於已將 MvcHandler類的ProcessRequest方法註冊到HttpApplication的事件中,因此事件的執行就觸發了此方法),下圖就是一個簡要的執行過程!html

public class ControllerActionInvoker : IActionInvoker
{
	protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
	{
		actionResult.ExecuteResult(controllerContext);
	}
}

  整個過程大體通過【Controller的激活】-->【Action的執行】-->【View的呈現】,由上圖可知,【View的呈現】是由ControllerActionInvoker類中的InvokeActionResult方法來觸發的!java

 2、ActionResult的建立

   概述中提到,【View的呈現】的功能就是:經過InvokeActionResult方法對【Action的執行】中生成的ActionResult進行處理。即:ActionResult是在【Action的執行】中建立的,建立方式有:git

  • 請求沒有經過Action的過濾器時,在過濾器的方法中建立一個ActionResult,將其看成最終的ActionResult,進行View的呈現
  • 請求經過全部過濾器,將Action方法返回的ActionResult看成最終的ActionResult,進行View的呈現。
    注:在Action方法中其實調用Controller類中的方法來進行建立ActionResult實例的,如:return Content("OK");等同於return new ContentResult(){ Content="OK"};

例、自定義個Action過濾器,當沒有經過時按照過濾器中定義的ActionResult進行View的呈現,具體執行過程下一部分介紹!web

public class MyActionFilter:FilterAttribute,IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext filterContext)
    { 
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.RouteData.DataTokens["OOK"] != "WuPeiqi")
        {
            ContentResult contentResult = new ContentResult();
            contentResult.Content = "DataToKens值有誤";
            filterContext.Result = contentResult;
        }
    }
}
//將此過濾器應用的Action上,那麼當請求中DataTokens的值不是否是相應的值時,就會用過濾器中的ContentResult對象來進行View的呈現,不然,就是利用Action方法Index中建立的ActionResult進行View的呈現!
public class HomeController : Controller
{
    [MyActionFilter]
    public ActionResult Index()
    {
        return Content("正確");
    }
}
View Code

3、View呈現過程分析

  ASP.NET MVC的【View的呈現】其實就是執行ActonResult的ExcuteResult方法!而接下來咱們介紹的就是這個ExcuteResult方法觸發了那些操做!!!在介紹以前咱們先來看看微軟提供了那些ActionResult!(ActionResult泛指那些繼承自System.Web.Mvc.ActionResult的類)
json

public abstract class ActionResult
{
    public abstract void ExecuteResult(ControllerContext context);
}
基類System.Web.Mvc.ActionResult
  • EmptyResult
  • ContentResult
  • FileResult
  • JavaScriptResult
  • JsonResult
  • HttpStatusCodeResult
  • RedirectResult
  • RedirectToRouteResult
  • ViewResult

  在ASP.NET MVC 的【Action的執行】中建立以上任意一個ActionResult對象,並執行該對象的ExcuteResult方法,從而進行【View的呈現】。這裏的最後一項ViewResult比較特殊,它的處理流程相對複雜,涉及到Razor引擎什麼的,以後詳細介紹!api

 下面就來看一些以上ActionResult的源碼,瞭解下【View的呈現】如何實現!數組

一、EmptyResult瀏覽器

public class EmptyResult : ActionResult
{
	private static readonly EmptyResult _singleton = new EmptyResult();

	internal static EmptyResult Instance
	{
		get { return _singleton; }
	}

	public override void ExecuteResult(ControllerContext context)
	{
	}
}

  由EmptyResult源碼可見,其ExecuteReuslt方法什麼都沒作,也就是該ActionReuslt的【View的呈現】部分不作任何操做,那麼此流程也就執行完畢。再看概述中的圖可知,接下來進行【對TempData再一次處理】-->【釋放Controller對象】,以後再繼續HttpApplication其餘的事件,包括對Session的處理、緩存的處理、對請求的返回等。緩存

 二、ContentResult

  ContentResult用於將字符串響應給客戶端!

public class ContentResult : ActionResult
{
	public string Content { get; set; }

	public Encoding ContentEncoding { get; set; }

	public string ContentType { get; set; }

	public override void ExecuteResult(ControllerContext context)
	{
		if (context == null)
		{
			throw new ArgumentNullException("context");
		}

		HttpResponseBase response = context.HttpContext.Response;

		if (!String.IsNullOrEmpty(ContentType))
		{
			response.ContentType = ContentType;
		}
		if (ContentEncoding != null)
		{
			response.ContentEncoding = ContentEncoding;
		}
		if (Content != null)
		{
			response.Write(Content);
		}
	}
}

  上述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)。

public class HttpResponseWrapper : HttpResponseBase
{
    private HttpResponse _httpResponse;
    //設置或獲取響應內容的編碼類型
    public override Encoding ContentEncoding
    {
        get
        {
            return this._httpResponse.ContentEncoding;
        }
        set
        {
            this._httpResponse.ContentEncoding = value;
        }
    } 
    
    public override string ContentType
    {
        get
        {
            return this._httpResponse.ContentType;
        }
        set
        {
            this._httpResponse.ContentType = value;
        }
    }
    public override void Write(string s)
    {
        this._httpResponse.Write(s);
    }
}
HttpResponseWrapper
public sealed class HttpResponse
{
    private TextWriter _writer;
    private Encoding _encoding;
    private string _contentType = "text/html";
    
    public Encoding ContentEncoding
    {
        get
        {    
            if (this._encoding == null)
            {
                //獲取webconfig文件中,globalization節點的值
                GlobalizationSection globalization = RuntimeConfig.GetLKGConfig(this._context).Globalization;
                if (globalization != null)
                {    
                    //設置Http響應的內容編碼
                    this._encoding = globalization.ResponseEncoding;
                }
                //沒有在globalization節點中配置編碼類型
                if (this._encoding == null)
                {    
                    //獲取操做系統的當前 ANSI 代碼頁的編碼並賦值給Http響應內容的編碼
                    this._encoding = Encoding.Default;
                }
            }
            return this._encoding;
        }
        set
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            //當沒有設置編碼類型或者編碼類型和原來的不相同時,根據value從新設定編碼類型
            if (this._encoding == null || !this._encoding.Equals(value))
            {
                this._encoding = value;
                this._encoder = null;
                if (this._httpWriter != null)
                {
                    //將HttpResponse類中與編碼相關的屬性值賦值到HttpWriter對象中與編碼相關的屬性
                    //以便HttpWriter輸出響應流時按照此編碼進行
                    this._httpWriter.UpdateResponseEncoding();
                }
            }
        }
    }
    
    public string ContentType
    {
        get
        {
            return this._contentType;
        }
        set
        {
            if (!this._headersWritten)
            {
                this._contentTypeSetByManagedCaller = true;
                this._contentType = value;
                return;
            }
            if (this._contentType == value)
            {
                return;
            }
            throw new HttpException(SR.GetString("Cannot_set_content_type_after_headers_sent"));
        }
    }    
    
    public void Write(string s)
    {
        this._writer.Write(s);
    }
}    
HttpResponse
public sealed class HttpWriter : TextWriter
{
    //根據編碼規則將字符串發送到 HTTP 輸出流
    public override void Write(string s)
    {
        if (this._ignoringFurtherWrites)
        {
            return;
        }
        if (s == null)
        {
            return;
        }
        if (s.Length != 0)
        {
            if (s.Length < this._charBufferFree)
            {
                StringUtil.UnsafeStringCopy(s, 0, this._charBuffer, this._charBufferLength - this._charBufferFree, s.Length);
                this._charBufferFree -= s.Length;
            }
            else
            {
                int i = s.Length;
                int num = 0;
                while (i > 0)
                {
                    if (this._charBufferFree == 0)
                    {
                        this.FlushCharBuffer(false);
                    }
                    int num2 = (i < this._charBufferFree) ? i : this._charBufferFree;
                    StringUtil.UnsafeStringCopy(s, num, this._charBuffer, this._charBufferLength - this._charBufferFree, num2);
                    this._charBufferFree -= num2;
                    num += num2;
                    i -= num2;
                }
            }
        }
        if (!this._responseBufferingOn)
        {
            //將信息寫入 HTTP 響應輸出流。
            this._response.Flush();
        }
    }
    //更新編碼相關的字段
    internal void UpdateResponseEncoding()
    {
        if (this._responseEncodingUpdated && this._charBufferLength != this._charBufferFree)
        {
            this.FlushCharBuffer(true);
        }
        this._responseEncoding = this._response.ContentEncoding;
        this._responseEncoder = this._response.ContentEncoder;
        this._responseCodePage = this._responseEncoding.CodePage;
        this._responseCodePageIsAsciiCompat = CodePageUtils.IsAsciiCompatibleCodePage(this._responseCodePage);
        this._responseEncodingUpdated = true;
    }    
    
}    
HttpWriter

在ASP.NET MVC 的Controller類中提供瞭如下三個建立ContentResult的重載,固然也能夠直接在Action中建立ContentReuslt對象並做爲方法的返回值。

public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
{
    //省略其餘方法
    protected internal ContentResult Content(string content)
    {
        return Content(content, null /* contentType */);
    }

    protected internal ContentResult Content(string content, string contentType)
    {
        return Content(content, contentType, null /* contentEncoding */);
    }

    protected internal virtual ContentResult Content(string content, string contentType, Encoding contentEncoding)
    {
        return new ContentResult
        {
            Content = content,
            ContentType = contentType,
            ContentEncoding = contentEncoding
        };
    }
}
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。

public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IISAPIRuntime2, IRegisteredObject
{
    public ISAPIRuntime()
    {
        //將該ISAPIRuntime對象放在應用程序的已註冊對象列表中
        HostingEnvironment.RegisterObject(this);
    }

    public int ProcessRequest(IntPtr ecb, int iWRType)
    {
        IntPtr intPtr = IntPtr.Zero;
        if (iWRType == 2)
        {
            intPtr = ecb;
            ecb = UnsafeNativeMethods.GetEcb(intPtr);
        }
        ISAPIWorkerRequest iSAPIWorkerRequest = null;
        int result;
        try
        {
            bool useOOP = iWRType == 1;
            //將ISAPI擴展控制塊(ECB)中Http請求相關的信息封裝到IsapiWorkerRequest對象中
            iSAPIWorkerRequest = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
            iSAPIWorkerRequest.Initialize();
            string appPathTranslated = iSAPIWorkerRequest.GetAppPathTranslated();
            string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
            if (appDomainAppPathInternal == null || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
            {    
                //ASP.NET運行時開始執行
                HttpRuntime.ProcessRequestNoDemand(iSAPIWorkerRequest);
                result = 0;
            }
            else
            {
                HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[]
                {
                    appDomainAppPathInternal,
                    appPathTranslated
                }));
                result = 1;
            }
        }
        //省略部分代碼
        return result;
    }
}
ISAPIRuntime
public sealed class HttpRuntime
{
    //靜態字段
    private static HttpRuntime _theRuntime;
    public HttpRuntime()
    {
    }
    //靜態構造函數
    static HttpRuntime()
    {
        HttpRuntime.s_autogenKeys = new byte[1024];
        HttpRuntime.DirectorySeparatorString = new string(Path.DirectorySeparatorChar, 1);
        HttpRuntime.DoubleDirectorySeparatorString = new string(Path.DirectorySeparatorChar, 2);
        HttpRuntime.s_InvalidPhysicalPathChars = new char[]
        {
            '/',
            '?',
            '*',
            '<',
            '>',
            '|',
            '"'
        };
        HttpRuntime.s_initialized = false;
        HttpRuntime.s_isEngineLoaded = false;
        HttpRuntime.s_factoryLock = new object();
        HttpRuntime.AddAppDomainTraceMessage("*HttpRuntime::cctor");
        HttpRuntime.StaticInit();
        HttpRuntime._theRuntime = new HttpRuntime();
        HttpRuntime._theRuntime.Init();
        HttpRuntime.AddAppDomainTraceMessage("HttpRuntime::cctor*");
    }

    internal static void ProcessRequestNoDemand(HttpWorkerRequest wr)
    {
        RequestQueue requestQueue = HttpRuntime._theRuntime._requestQueue;
        wr.UpdateInitialCounters();
        if (requestQueue != null)
        {
            wr = requestQueue.GetRequestToExecute(wr);
        }
        if (wr != null)
        {
            HttpRuntime.CalculateWaitTimeAndUpdatePerfCounter(wr);
            wr.ResetStartTime();
            //繼續執行
            HttpRuntime.ProcessRequestNow(wr);
        }
    }
    internal static void ProcessRequestNow(HttpWorkerRequest wr)
    {
        //繼續執行
        HttpRuntime._theRuntime.ProcessRequestInternal(wr);
    }

    private void ProcessRequestInternal(HttpWorkerRequest wr)
    {
        Interlocked.Increment(ref this._activeRequestCount);
        if (this._disposingHttpRuntime)
        {
            try
            {
                wr.SendStatus(503, "Server Too Busy");
                wr.SendKnownResponseHeader(12, "text/html; charset=utf-8");
                byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>");
                wr.SendResponseFromMemory(bytes, bytes.Length);
                wr.FlushResponse(true);
                wr.EndOfRequest();
            }
            finally
            {
                Interlocked.Decrement(ref this._activeRequestCount);
            }
            return;
        }
        HttpContext httpContext;
        try
        {
            //建立請求上下文,繼續執行
            httpContext = new HttpContext(wr, false);
        }
        catch
        {
            try
            {
                wr.SendStatus(400, "Bad Request");
                wr.SendKnownResponseHeader(12, "text/html; charset=utf-8");
                byte[] bytes2 = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
                wr.SendResponseFromMemory(bytes2, bytes2.Length);
                wr.FlushResponse(true);
                wr.EndOfRequest();
                return;
            }
            finally
            {
                Interlocked.Decrement(ref this._activeRequestCount);
            }
        }
        wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, httpContext);
        HostingEnvironment.IncrementBusyCount();
        try
        {
            try
            {
                this.EnsureFirstRequestInit(httpContext);
            }
            catch
            {
                if (!httpContext.Request.IsDebuggingRequest)
                {
                    throw;
                }
            }
            //初始化HttpResponse的TextWriter
            httpContext.Response.InitResponseWriter();
            //經過 HttpApplicationFactory獲取HttpApplication實例
            IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(httpContext);
            if (applicationInstance == null)
            {
                throw new HttpException(SR.GetString("Unable_create_app_object"));
            }
            if (EtwTrace.IsTraceEnabled(5, 1))
            {
                EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, httpContext.WorkerRequest, applicationInstance.GetType().FullName, "Start");
            }
            if (applicationInstance is IHttpAsyncHandler)
            {
                IHttpAsyncHandler httpAsyncHandler = (IHttpAsyncHandler)applicationInstance;
                httpContext.AsyncAppHandler = httpAsyncHandler;
                httpAsyncHandler.BeginProcessRequest(httpContext, this._handlerCompletionCallback, httpContext);
            }
            else
            {
                applicationInstance.ProcessRequest(httpContext);
                this.FinishRequest(httpContext.WorkerRequest, httpContext, null);
            }
        }
        catch (Exception e)
        {
            httpContext.Response.InitResponseWriter();
            this.FinishRequest(wr, httpContext, e);
        }
    }

}
HttpRuntime
public sealed class HttpContext : IServiceProvider, IPrincipalContainer
{
    //構造函數
    public HttpContext(HttpWorkerRequest wr)
    {
        this._wr = wr;
        //初始化HttpContext並建立HttpRequest和HttpResponse
        this.Init(new HttpRequest(wr, this), new HttpResponse(wr, this));
        //初始化HttpResponse的TextWriter
        this._response.InitResponseWriter();
    }
    private void Init(HttpRequest request, HttpResponse response)
    {
        this._request = request;
        this._response = response;
        //省略其餘代碼
    }
}
HttpContext

 三、FileResult

  FileResult用於將某個物理文件的內容響應給客戶端!

public abstract class FileResult : ActionResult
{
    private string _fileDownloadName;

    protected FileResult(string contentType)
    {
        if (String.IsNullOrEmpty(contentType))
        {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentType");
        }

        ContentType = contentType;
    }

    public string ContentType { get; private set; }

    public string FileDownloadName
    {
        get { return _fileDownloadName ?? String.Empty; }
        set { _fileDownloadName = value; }
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        HttpResponseBase response = context.HttpContext.Response;
        //response.ContentType默認爲「text/html」
        response.ContentType = ContentType;
        //若是沒有指定文件被下載的名稱,則按照內聯的方法輸出文件,不然按照附件的形式。
        if (!String.IsNullOrEmpty(FileDownloadName))
        {
            //處理文件名 並 構造「Content-Disposition」的報頭的值
            //例如:文件名中包含Unicode碼或包含特殊符號等
            string headerValue = ContentDispositionUtil.GetHeaderValue(FileDownloadName);
            //採用附件形式,須要爲響應建立一個名稱爲「Content-Disposition」的報頭,該報頭的值格式爲「attachment;filename={文件名}」
            context.HttpContext.Response.AddHeader("Content-Disposition", headerValue);
        }

        WriteFile(response);
    }

    protected abstract void WriteFile(HttpResponseBase response);
    
    //處理文件名並構造 「Content-Disposition」的報頭的值
    internal static class ContentDispositionUtil
    {
        private const string HexDigits = "0123456789ABCDEF";

        private static void AddByteToStringBuilder(byte b, StringBuilder builder)
        {
            builder.Append('%');

            int i = b;
            AddHexDigitToStringBuilder(i >> 4, builder);
            AddHexDigitToStringBuilder(i % 16, builder);
        }

        private static void AddHexDigitToStringBuilder(int digit, StringBuilder builder)
        {
            builder.Append(HexDigits[digit]);
        }

        private static string CreateRfc2231HeaderValue(string filename)
        {
            StringBuilder builder = new StringBuilder("attachment; filename*=UTF-8''");

            byte[] filenameBytes = Encoding.UTF8.GetBytes(filename);
            foreach (byte b in filenameBytes)
            {
                if (IsByteValidHeaderValueCharacter(b))
                {
                    builder.Append((char)b);
                }
                else
                {
                    AddByteToStringBuilder(b, builder);
                }
            }

            return builder.ToString();
        }

        public static string GetHeaderValue(string fileName)
        {
            // If fileName contains any Unicode characters, encode according
            // to RFC 2231 (with clarifications from RFC 5987)
            foreach (char c in fileName)
            {
                if ((int)c > 127)
                {
                    return CreateRfc2231HeaderValue(fileName);
                }
            }

            // Knowing there are no Unicode characters in this fileName, rely on
            // ContentDisposition.ToString() to encode properly.
            // In .Net 4.0, ContentDisposition.ToString() throws FormatException if
            // the file name contains Unicode characters.
            // In .Net 4.5, ContentDisposition.ToString() no longer throws FormatException
            // if it contains Unicode, and it will not encode Unicode as we require here.
            // The Unicode test above is identical to the 4.0 FormatException test,
            // allowing this helper to give the same results in 4.0 and 4.5.         
            ContentDisposition disposition = new ContentDisposition() { FileName = fileName };
            return disposition.ToString();
        }

        // Application of RFC 2231 Encoding to Hypertext Transfer Protocol (HTTP) Header Fields, sec. 3.2
        // http://greenbytes.de/tech/webdav/draft-reschke-rfc2231-in-http-latest.html
        private static bool IsByteValidHeaderValueCharacter(byte b)
        {
            if ((byte)'0' <= b && b <= (byte)'9')
            {
                return true; // is digit
            }
            if ((byte)'a' <= b && b <= (byte)'z')
            {
                return true; // lowercase letter
            }
            if ((byte)'A' <= b && b <= (byte)'Z')
            {
                return true; // uppercase letter
            }

            switch (b)
            {
                case (byte)'-':
                case (byte)'.':
                case (byte)'_':
                case (byte)'~':
                case (byte)':':
                case (byte)'!':
                case (byte)'$':
                case (byte)'&':
                case (byte)'+':
                    return true;
            }

            return false;
        }
    }
}

  對於FileResult,具備一個表示媒體類型的只讀屬性ContentType,該屬性在構造函數中被初始化。當咱們基於某個物理文件建立相應的FileReuslt對象的時候應該根據文件的類型指定該媒體類型屬性,例如:目標文件是.jpg圖片,那麼對應的媒體類型應該是「image/jpeg」;對於一個.pdf文件,則採用「application/pdf」。

  對於FileResult,還具備一個表示下載文件名的屬性FileDownloadName,若是該屬性沒有指定或者設置的值爲null,則會按照內聯的方式利用瀏覽器直接打開響應的文件,不然會以附件的形式被下載而且文件名爲屬性FileDownloadName的值。(查看FileResult源碼可知,內聯和附件的區別是響應是否包含「Content-Disposition」報頭)
  FileReult僅僅是一個抽象類,對於文件內容的輸出實如今抽象方法WriteFile方法中。FileResult有三個派生類實現了WriterFile方法分別是:

public class FileContentResult : FileResult
{
    //參數爲字節數組、響應的媒體類型
    public FileContentResult(byte[] fileContents, string contentType)
        : base(contentType)
    {
        if (fileContents == null)
        {
            throw new ArgumentNullException("fileContents");
        }

        FileContents = fileContents;
    }

    public byte[] FileContents { get; private set; }

    protected override void WriteFile(HttpResponseBase response)
    {
        //將字節數組輸出
        response.OutputStream.Write(FileContents, 0, FileContents.Length);
    }
}
FileContentResult
public class FileStreamResult : FileResult
{
    // default buffer size as defined in BufferedStream type
    private const int BufferSize = 0x1000;
    //參數爲:文件流、媒體類型
    public FileStreamResult(Stream fileStream, string contentType)
        : base(contentType)
    {
        if (fileStream == null)
        {
            throw new ArgumentNullException("fileStream");
        }

        FileStream = fileStream;
    }

    public Stream FileStream { get; private set; }

    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
public class FilePathResult : FileResult
{
    //參數爲:文件路徑、媒體類型
    public FilePathResult(string fileName, string contentType)
        : base(contentType)
    {
        if (String.IsNullOrEmpty(fileName))
        {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "fileName");
        }

        FileName = fileName;
    }

    public string FileName { get; private set; }

    protected override void WriteFile(HttpResponseBase response)
    {
        response.TransmitFile(FileName);
    }
}
FilePathResult

  以上的三個繼承自FileResult的類,最終都是經過 文件的字節數組 的形式發送到Http輸出流,不一樣的是做爲開發者其起始點不一,FileContentResult傳入字節數組而後將內容寫入當前Http響應的輸出流,FileStreamReuslt傳入數據流,以後內部存入字節數組再將內容寫入當前Http響應的輸出流,FilePathResult傳入文件地址,以後內部讀取文件並存入字節數組再將內容寫入當前Http響應的輸出流。

 在ASP.NET MVC 的Controller類中提供了建立以上三個FileResult派生類的對象的重載,固然也能夠直接在Action中建立相應的FileReuslt對象並做爲方法的返回值。

public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
{
    protected internal FileContentResult File(byte[] fileContents, string contentType)
    {
        return File(fileContents, contentType, null /* fileDownloadName */);
    }

    protected internal virtual FileContentResult File(byte[] fileContents, string contentType, string fileDownloadName)
    {
        return new FileContentResult(fileContents, contentType) { FileDownloadName = fileDownloadName };
    }

    protected internal FileStreamResult File(Stream fileStream, string contentType)
    {
        return File(fileStream, contentType, null /* fileDownloadName */);
    }

    protected internal virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName)
    {
        return new FileStreamResult(fileStream, contentType) { FileDownloadName = fileDownloadName };
    }

    protected internal FilePathResult File(string fileName, string contentType)
    {
        return File(fileName, contentType, null /* fileDownloadName */);
    }

    protected internal virtual FilePathResult File(string fileName, string contentType, string fileDownloadName)
    {
        return new FilePathResult(fileName, contentType) { FileDownloadName = fileDownloadName };
    }
}
Controller

 四、JavaScriptResult

  在後臺動態的以字符串形式傳入一段JavaScript腳本,並做爲請求的響應使得腳本在客戶端被執行!

public class JavaScriptResult : ActionResult
{
	public string Script { get; set; }

	public override void ExecuteResult(ControllerContext context)
	{
		if (context == null)
		{
			throw new ArgumentNullException("context");
		}

		HttpResponseBase response = context.HttpContext.Response;
		//指定響應的媒體類型
		response.ContentType = "application/x-javascript";

		if (Script != null)
		{
			response.Write(Script);
		}
	}
}

  經過JavaScriptResult源碼能夠看出,其輸出方式和ContentResult相同,不一樣的只是在JavaScriptResult中內部指定了輸出的媒體類型爲「application/x-javascript」(也能夠是「text/javascript」),而咱們也能夠經過設置ContentResult的輸出媒體類型來實現與JavaScriptResult相同的功能!

  在ASP.NET MVC 的Controller類中提供了建立JavaScriptResult對象的方法,固然也能夠直接在Action中建立JavaScriptResult對象並做爲方法的返回值。

public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
{
    //省略其餘代碼
    protected internal virtual JavaScriptResult JavaScript(string script)
    {
        return new JavaScriptResult { Script = script };
    }
}
Controller

五、JsonResult

  JsonResutl用於以Json的格式返回響應的數據!

public class JsonResult : ActionResult
{
	public JsonResult()
	{
		//定義枚舉類型,默認拒絕Get請求的響應
		JsonRequestBehavior = JsonRequestBehavior.DenyGet;
	}

	public Encoding ContentEncoding { get; set; }

	public string ContentType { get; set; }

	public object Data { get; set; }

	//是否決絕Http Get請求(默認拒絕---構造函數中定義)
	public JsonRequestBehavior JsonRequestBehavior { get; set; }

	/// <summary>
	///指定 JSON 字符串的最大長度(UTF-8 字符的最大數量)。 默認長度爲 102400。
	/// </summary>
	public int? MaxJsonLength { get; set; }

	/// <summary>
	/// 指定要序列化類型的最大深度。 默認的遞歸限制爲 100。
	/// </summary>
	public int? RecursionLimit { get; set; }

	public override void ExecuteResult(ControllerContext context)
	{
		if (context == null)
		{
			throw new ArgumentNullException("context");
		}
		//若是拒絕Get請求&&發送來的請求也是Get方式
		if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
			String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
		{
			throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);
		}

		HttpResponseBase response = context.HttpContext.Response;

		//默認媒體類型爲"application/json"
		if (!String.IsNullOrEmpty(ContentType))
		{
			response.ContentType = ContentType;
		}
		else
		{
			response.ContentType = "application/json";
		}
		//編碼類型的選取仍是和ContentResult中同樣,優先級:顯示設定>WebConfig中節點>Encoding.Default
		if (ContentEncoding != null)
		{
			response.ContentEncoding = ContentEncoding;
		}
		if (Data != null)
		{
			//經過JavaScriptSerializer來將CLR對象序列化成Json格式字符串
			JavaScriptSerializer serializer = new JavaScriptSerializer();
			if (MaxJsonLength.HasValue)
			{
				//serializer.MaxJsonLength是JSON 字符串的最大長度(UTF-8 字符的最大數量)。 默認長度爲 102400
				serializer.MaxJsonLength = MaxJsonLength.Value;
			}
			if (RecursionLimit.HasValue)
			{
				//serializer.RecursionLimit是指要序列化類型的最大深度。 默認的遞歸限制爲 100
				serializer.RecursionLimit = RecursionLimit.Value;
			}
			//將Json格式的字符串寫入當前Http響應的輸出流
			response.Write(serializer.Serialize(Data));
		}
	}
}
public enum JsonRequestBehavior
{
	AllowGet,
	DenyGet,
}

  對於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中設置。更多設置

<configuration>
  <system.web.extensions>
    <scripting>
      <webServices>
        <jsonSerialization maxJsonLength="5000"/>
      </webServices>
    </scripting>
  </system.web.extensions>
</configuration>

  在ASP.NET MVC 的Controller類中提供了一下建立JsonResult對象的方法,固然也能夠直接在Action中建立JsonResult對象並做爲方法的返回值。

public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
{
    //省略其餘代碼
    protected internal JsonResult Json(object data)
    {
        return Json(data, null /* contentType */, null /* contentEncoding */, JsonRequestBehavior.DenyGet);
    }

    protected internal JsonResult Json(object data, string contentType)
    {
        return Json(data, contentType, null /* contentEncoding */, JsonRequestBehavior.DenyGet);
    }

    protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding)
    {
        return Json(data, contentType, contentEncoding, JsonRequestBehavior.DenyGet);
    }

    protected internal JsonResult Json(object data, JsonRequestBehavior behavior)
    {
        return Json(data, null /* contentType */, null /* contentEncoding */, behavior);
    }

    protected internal JsonResult Json(object data, string contentType, JsonRequestBehavior behavior)
    {
        return Json(data, contentType, null /* contentEncoding */, behavior);
    }

    protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)
    {
        return new JsonResult
        {
            Data = data,
            ContentType = contentType,
            ContentEncoding = contentEncoding,
            JsonRequestBehavior = behavior
        };
    }
}
Controller

六、HttpStatusCodeResult

  HttpStatusCodeResult用於返回對Http請求響應狀態的代碼和一個可選的狀態描述!

public class HttpStatusCodeResult : ActionResult
{
	public HttpStatusCodeResult(int statusCode)
		: this(statusCode, null)
	{
	}
	//HttStatusCode是個枚舉類型,用於定義狀態代碼
	public HttpStatusCodeResult(HttpStatusCode statusCode)
		: this(statusCode, null)
	{
	}

	public HttpStatusCodeResult(HttpStatusCode statusCode, string statusDescription)
		: this((int)statusCode, statusDescription)
	{
	}

	public HttpStatusCodeResult(int statusCode, string statusDescription)
	{
		StatusCode = statusCode;
		StatusDescription = statusDescription;
	}
	//響應狀態代碼
	public int StatusCode { get; private set; }
	//響應狀態描述
	public string StatusDescription { get; private set; }

	public override void ExecuteResult(ControllerContext context)
	{
		if (context == null)
		{
			throw new ArgumentNullException("context");
		}
                //默認狀態代碼爲:200
		context.HttpContext.Response.StatusCode = StatusCode;
		if (StatusDescription != null)
		{
			context.HttpContext.Response.StatusDescription = StatusDescription;
		}
	}
}

  HttpStatusCodeResult爲Http的響應頭設置狀態代碼和狀態描述,設置時,能夠經過構造函數傳入值也能夠經過給屬性賦值來操做。對於HttpStatustCodeResult的構造函數中HttpStatusCode類型的參數,它是一個枚舉類型,其中包含了衆多Http響應頭狀態。
  值得一說的是,若是咱們採用Visual StudioDvelopment Server做爲Web應用的宿主,經過HttpStatusCodeResult的StatusDescription屬性設置的狀態描述信息不會反映在Http響應中,只有採用IIS做爲宿主纔會真正將此信息寫入響應消息。

public enum HttpStatusCode
{
    Continue = 100,
    SwitchingProtocols,
    OK = 200,
    Created,
    Accepted,
    NonAuthoritativeInformation,
    NoContent,
    ResetContent,
    PartialContent,
    MultipleChoices = 300,
    Ambiguous = 300,
    MovedPermanently,
    Moved = 301,
    Found,
    Redirect = 302,
    SeeOther,
    RedirectMethod = 303,
    NotModified,
    UseProxy,
    Unused,
    TemporaryRedirect,
    RedirectKeepVerb = 307,
    BadRequest = 400,
    Unauthorized,
    PaymentRequired,
    Forbidden,
    NotFound,
    MethodNotAllowed,
    NotAcceptable,
    ProxyAuthenticationRequired,
    RequestTimeout,
    Conflict,
    Gone,
    LengthRequired,
    PreconditionFailed,
    RequestEntityTooLarge,
    RequestUriTooLong,
    UnsupportedMediaType,
    RequestedRangeNotSatisfiable,
    ExpectationFailed,
    UpgradeRequired = 426,
    InternalServerError = 500,
    NotImplemented,
    BadGateway,
    ServiceUnavailable,
    GatewayTimeout,
    HttpVersionNotSupported
}
HttpStatusCode

  ASP.NET MVC中有兩個繼承自HttpStatusCodeResult的類,即:HttpNotFoundResult和AuthorizeAttribute,用於指定特定相應狀態和狀態描述,本質上仍是執行HttpStatusCodeResult來完成,只不過在內部爲HttpStatuCodeResult指定了響應狀態,分別是40四、401。

public class HttpNotFoundResult : HttpStatusCodeResult
{
    public HttpNotFoundResult()
        : this(null)
    {
    }

    // NotFound is equivalent to HTTP status 404.
    public HttpNotFoundResult(string statusDescription)
        : base(HttpStatusCode.NotFound, statusDescription)
    {
    }
}
HttpNotFoundResult
public class HttpUnauthorizedResult : HttpStatusCodeResult
{
    public HttpUnauthorizedResult()
        : this(null)
    {
    }

    // Unauthorized is equivalent to HTTP status 401, the status code for unauthorized
    // access. Other code might intercept this and perform some special logic. For
    // example, the FormsAuthenticationModule looks for 401 responses and instead
    // redirects the user to the login page.
    public HttpUnauthorizedResult(string statusDescription)
        : base(HttpStatusCode.Unauthorized, statusDescription)
    {
    }
}
HttpUnauthorizedResult

七、RedirecteResult

  RedirectResult用於實現針對某個地址的重定向!

public class RedirectResult : ActionResult
{
	public RedirectResult(string url)
		: this(url, permanent: false)
	{
	}

	public RedirectResult(string url, bool permanent)
	{
		if (String.IsNullOrEmpty(url))
		{
			throw new ArgumentException(MvcResources.Common_NullOrEmpty, "url");
		}

		Permanent = permanent;
		Url = url;
	}
	//是否永久重定向,默認爲否。(永久重定向的Http狀態碼爲301,不然是暫時重定向Http狀態碼爲302)
	public bool Permanent { get; private set; }
	//要跳轉的地址(相對地址或絕對地址)
	public string Url { get; private set; }

	public override void ExecuteResult(ControllerContext context)
	{
		if (context == null)
		{
			throw new ArgumentNullException("context");
		}
		if (context.IsChildAction)
		{
			throw new InvalidOperationException(MvcResources.RedirectAction_CannotRedirectInChildAction);
		}
		//處理Url地址,相對地址的處理。
		string destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
		context.Controller.TempData.Keep();
		//是否永久重定向
		if (Permanent)
		{
			context.HttpContext.Response.RedirectPermanent(destinationUrl, endResponse: false);
		}
		else
		{
			context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
		}
	}
}

  對於RedirectResult,能夠定義暫時重定向(302重定向)和永久重定向(301重定向),兩種重定向的不一樣做用主要體如今SEO上,搜索引擎會使用永久重定向目標地址更新本身的索引,而暫時重定向則不會。另外,永久重定向是在ASP.NET 4以後引進的,在以前若是想要實現永久重定向的話,須要本身來設置Http響應狀態碼爲301。
  對於UrlHelper.GenerateCotentUrl方法,用來處理Url。當定義的Url爲相對地址時,如:~/xxx/xxx,該方法會利用請求上下文來補全地址。

public static string GenerateContentUrl(string contentPath, HttpContextBase httpContext)
{
	if (string.IsNullOrEmpty(contentPath))
	{
		throw new ArgumentException(MvcResources.Common_NullOrEmpty, "contentPath");
	}
	if (httpContext == null)
	{
		throw new ArgumentNullException("httpContext");
	}
	if (contentPath[0] == '~')
	{
		return PathHelpers.GenerateClientUrl(httpContext, contentPath);
	}
	return contentPath;
}

  對於ASP.NET MVC的Controller類中定義了一下幾個方法來建立RedirectResult,然也能夠直接在Action中建立RedirectResult對象並做爲方法的返回值。

public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
{
    //省略其餘代碼
    protected internal virtual RedirectResult Redirect(string url)
    {
        if (String.IsNullOrEmpty(url))
        {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "url");
        }

        return new RedirectResult(url);
    }

    protected internal virtual RedirectResult RedirectPermanent(string url)
    {
        if (String.IsNullOrEmpty(url))
        {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "url");
        }

        return new RedirectResult(url, permanent: true);
    }
}
Controller

八、RedirectToRoutResult

  RedirectToRouteResult用於將路由信息中的Controller和Action拼接成Url,再進行跳轉!

public class RedirectToRouteResult : ActionResult
{
	private RouteCollection _routes;

	public RedirectToRouteResult(RouteValueDictionary routeValues)
		:
			this(null, routeValues)
	{
	}

	public RedirectToRouteResult(string routeName, RouteValueDictionary routeValues)
		: this(routeName, routeValues, permanent: false)
	{
	}

	public RedirectToRouteResult(string routeName, RouteValueDictionary routeValues, bool permanent)
	{
		Permanent = permanent;
		RouteName = routeName ?? String.Empty;
		RouteValues = routeValues ?? new RouteValueDictionary();
	}

	public bool Permanent { get; private set; }

	public string RouteName { get; private set; }

	public RouteValueDictionary RouteValues { get; private set; }

	internal RouteCollection Routes
	{
		get
		{
			if (_routes == null)
			{
				_routes = RouteTable.Routes;
			}
			return _routes;
		}
		set { _routes = value; }
	}

	public override void ExecuteResult(ControllerContext context)
	{
		if (context == null)
		{
			throw new ArgumentNullException("context");
		}
		if (context.IsChildAction)
		{
			throw new InvalidOperationException(MvcResources.RedirectAction_CannotRedirectInChildAction);
		}

		string destinationUrl = UrlHelper.GenerateUrl(RouteName, null /* actionName */, null /* controllerName */, RouteValues, Routes, context.RequestContext, false /* includeImplicitMvcValues */);
		if (String.IsNullOrEmpty(destinationUrl))
		{
			throw new InvalidOperationException(MvcResources.Common_NoRouteMatched);
		}

		context.Controller.TempData.Keep();

		if (Permanent)
		{
			context.HttpContext.Response.RedirectPermanent(destinationUrl, endResponse: false);
		}
		else
		{
			context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
		}
	}
}

  RedirectToRouteResult和RedirectResult都是實現重定向,只不過RedirectToRouteResult的跳轉地址是經過路由信息中的Controller和Action的拼接來完成的,其餘均和RedirectResult相同!

  ASP.NET MVC在Controller類中定義了幾個方法用於建立RedirectToRouteResult對象,固然也能夠直接在Action中建立RedirectToRouteResult對象並做爲方法的返回值。

public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
{
    //省略其餘代碼
    protected internal RedirectToRouteResult RedirectToAction(string actionName)
    {
        return RedirectToAction(actionName, (RouteValueDictionary)null);
    }

    protected internal RedirectToRouteResult RedirectToAction(string actionName, object routeValues)
    {
        return RedirectToAction(actionName, new RouteValueDictionary(routeValues));
    }

    protected internal RedirectToRouteResult RedirectToAction(string actionName, RouteValueDictionary routeValues)
    {
        return RedirectToAction(actionName, null /* controllerName */, routeValues);
    }

    protected internal RedirectToRouteResult RedirectToAction(string actionName, string controllerName)
    {
        return RedirectToAction(actionName, controllerName, (RouteValueDictionary)null);
    }

    protected internal RedirectToRouteResult RedirectToAction(string actionName, string controllerName, object routeValues)
    {
        return RedirectToAction(actionName, controllerName, new RouteValueDictionary(routeValues));
    }

    protected internal virtual RedirectToRouteResult RedirectToAction(string actionName, string controllerName, RouteValueDictionary routeValues)
    {
        RouteValueDictionary mergedRouteValues;

        if (RouteData == null)
        {
            mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, null, routeValues, includeImplicitMvcValues: true);
        }
        else
        {
            mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, RouteData.Values, routeValues, includeImplicitMvcValues: true);
        }

        return new RedirectToRouteResult(mergedRouteValues);
    }

    protected internal RedirectToRouteResult RedirectToActionPermanent(string actionName)
    {
        return RedirectToActionPermanent(actionName, (RouteValueDictionary)null);
    }

    protected internal RedirectToRouteResult RedirectToActionPermanent(string actionName, object routeValues)
    {
        return RedirectToActionPermanent(actionName, new RouteValueDictionary(routeValues));
    }

    protected internal RedirectToRouteResult RedirectToActionPermanent(string actionName, RouteValueDictionary routeValues)
    {
        return RedirectToActionPermanent(actionName, null /* controllerName */, routeValues);
    }

    protected internal RedirectToRouteResult RedirectToActionPermanent(string actionName, string controllerName)
    {
        return RedirectToActionPermanent(actionName, controllerName, (RouteValueDictionary)null);
    }

    protected internal RedirectToRouteResult RedirectToActionPermanent(string actionName, string controllerName, object routeValues)
    {
        return RedirectToActionPermanent(actionName, controllerName, new RouteValueDictionary(routeValues));
    }

    protected internal virtual RedirectToRouteResult RedirectToActionPermanent(string actionName, string controllerName, RouteValueDictionary routeValues)
    {
        RouteValueDictionary implicitRouteValues = (RouteData != null) ? RouteData.Values : null;

        RouteValueDictionary mergedRouteValues =
            RouteValuesHelpers.MergeRouteValues(actionName, controllerName, implicitRouteValues, routeValues, includeImplicitMvcValues: true);

        return new RedirectToRouteResult(null, mergedRouteValues, permanent: true);
    }

    protected internal RedirectToRouteResult RedirectToRoute(object routeValues)
    {
        return RedirectToRoute(new RouteValueDictionary(routeValues));
    }

    protected internal RedirectToRouteResult RedirectToRoute(RouteValueDictionary routeValues)
    {
        return RedirectToRoute(null /* routeName */, routeValues);
    }

    protected internal RedirectToRouteResult RedirectToRoute(string routeName)
    {
        return RedirectToRoute(routeName, (RouteValueDictionary)null);
    }

    protected internal RedirectToRouteResult RedirectToRoute(string routeName, object routeValues)
    {
        return RedirectToRoute(routeName, new RouteValueDictionary(routeValues));
    }

    protected internal virtual RedirectToRouteResult RedirectToRoute(string routeName, RouteValueDictionary routeValues)
    {
        return new RedirectToRouteResult(routeName, RouteValuesHelpers.GetRouteValues(routeValues));
    }

    protected internal RedirectToRouteResult RedirectToRoutePermanent(object routeValues)
    {
        return RedirectToRoutePermanent(new RouteValueDictionary(routeValues));
    }

    protected internal RedirectToRouteResult RedirectToRoutePermanent(RouteValueDictionary routeValues)
    {
        return RedirectToRoutePermanent(null /* routeName */, routeValues);
    }

    protected internal RedirectToRouteResult RedirectToRoutePermanent(string routeName)
    {
        return RedirectToRoutePermanent(routeName, (RouteValueDictionary)null);
    }

    protected internal RedirectToRouteResult RedirectToRoutePermanent(string routeName, object routeValues)
    {
        return RedirectToRoutePermanent(routeName, new RouteValueDictionary(routeValues));
    }

    protected internal virtual RedirectToRouteResult RedirectToRoutePermanent(string routeName, RouteValueDictionary routeValues)
    {
        return new RedirectToRouteResult(routeName, RouteValuesHelpers.GetRouteValues(routeValues), permanent: true);
    }

}
Controller

九、ViewResult

  ViewResult內容包含了:PartialViewResult和ViewResult。ViewResult將視圖頁的內容響應給客戶端,而PartialViewResult稱分部視圖,其響應請求時不輸出那寫html、head、body等標籤,只是將分部視圖中內容返回!因爲ViewResult和PartialViewResult在進行【View呈現】的過程大體相同,因此此處就只針對ViewResult進行詳細解讀,而PartialViewRsult詳細過程將再也不敖述。(分部視圖的更多信息:關於如何PartialViewResult的使用

public abstract class ViewResultBase : ActionResult
{
    private DynamicViewDataDictionary _dynamicViewData;
    private TempDataDictionary _tempData;
    private ViewDataDictionary _viewData;
    private ViewEngineCollection _viewEngineCollection;
    private string _viewName;

    public object Model
    {
        get { return ViewData.Model; }
    }

    public TempDataDictionary TempData
    {
        get
        {
            if (_tempData == null)
            {
                _tempData = new TempDataDictionary();
            }
            return _tempData;
        }
        set { _tempData = value; }
    }

    public IView View { get; set; }

    public dynamic ViewBag
    {
        get
        {
            if (_dynamicViewData == null)
            {
                _dynamicViewData = new DynamicViewDataDictionary(() => ViewData);
            }
            return _dynamicViewData;
        }
    }
    public ViewDataDictionary ViewData
    {
        get
        {
            if (_viewData == null)
            {
                _viewData = new ViewDataDictionary();
            }
            return _viewData;
        }
        set { _viewData = value; }
    }

    //獲取或設置視圖引擎,ASP.NET有兩個視圖引擎,分別是:WebFormViewEngine、RazorViewEngine。
    public ViewEngineCollection ViewEngineCollection
    {
        get { return _viewEngineCollection ?? ViewEngines.Engines; }
        set { _viewEngineCollection = value; }
    }

    public string ViewName
    {
        get { return _viewName ?? String.Empty; }
        set { _viewName = value; }
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        //若是沒有設置ViewName就將當前Action做爲ViewName
        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);
        }
    }

    protected abstract ViewEngineResult FindView(ControllerContext context);
}
ViewResultBase
public class ViewResult : ViewResultBase
{
    private string _masterName;

    public string MasterName
    {
        get { return _masterName ?? String.Empty; }
        set { _masterName = value; }
    }

    protected override ViewEngineResult FindView(ControllerContext context)
    {
        //根據View引擎去尋找View
        //此處ViewEngineCollection是ViewResultBase類中的一個屬性,表示視圖引擎集合。
        ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);
        //若是找到了指定的VIew,則返回。
        if (result.View != null)
        {
            return result;
        }
        //沒有找到指定的View,那麼就將查找路徑給經過異常返回。
        StringBuilder locationsText = new StringBuilder();
        foreach (string location in result.SearchedLocations)
        {
            locationsText.AppendLine();
            locationsText.Append(location);
        }
        throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
                                                          MvcResources.Common_ViewNotFound, ViewName, locationsText));
    }
}
ViewResult
public class PartialViewResult : ViewResultBase
{
    /// <summary>Returns the <see cref="T:System.Web.Mvc.ViewEngineResult" /> object that is used to render the view.</summary>
    /// <returns>The view engine result.</returns>
    /// <param name="context">The controller context.</param>
    /// <exception cref="T:System.InvalidOperationException">An error occurred while the method was attempting to find the view.</exception>
    protected override ViewEngineResult FindView(ControllerContext context)
    {
        ViewEngineResult viewEngineResult = base.ViewEngineCollection.FindPartialView(context, base.ViewName);
        if (viewEngineResult.View != null)
        {
            return viewEngineResult;
        }
        StringBuilder stringBuilder = new StringBuilder();
        foreach (string current in viewEngineResult.SearchedLocations)
        {
            stringBuilder.AppendLine();
            stringBuilder.Append(current);
        }
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.Common_PartialViewNotFound, new object[]
        {
            base.ViewName,
            stringBuilder
        }));
    }
}
PartialViewResult

Controller類中定義的建立ViewResult和PartialViewResult對象的方法:

public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer
{
    //省略其餘代碼...
    
    //PartialViewResult
    protected internal PartialViewResult PartialView()
    {
        return this.PartialView(null, null);
    }
    protected internal PartialViewResult PartialView(object model)
    {
        return this.PartialView(null, model);
    }
    protected internal PartialViewResult PartialView(string viewName)
    {
        return this.PartialView(viewName, null);
    }
    protected internal virtual PartialViewResult PartialView(string viewName, object model)
    {
        if (model != null)
        {
            base.ViewData.Model = model;
        }
        return new PartialViewResult
        {
            ViewName = viewName,
            ViewData = base.ViewData,
            TempData = base.TempData,
            ViewEngineCollection = this.ViewEngineCollection
        };
    }
    //ViewResult
    protected internal ViewResult View()
    {
        string viewName = null;
        string masterName = null;
        object model = null;
        return this.View(viewName, masterName, model);
    }
    protected internal ViewResult View(object model)
    {
        return this.View(null, null, model);
    }
    protected internal ViewResult View(string viewName)
    {
        string masterName = null;
        object model = null;
        return this.View(viewName, masterName, model);
    }
    protected internal ViewResult View(string viewName, string masterName)
    {
        return this.View(viewName, masterName, null);
    }
    protected internal ViewResult View(string viewName, object model)
    {
        return this.View(viewName, null, model);
    }
    protected internal virtual ViewResult View(string viewName, string masterName, object model)
    {
        if (model != null)
        {
            base.ViewData.Model = model;
        }
        return new ViewResult
        {
            ViewName = viewName,
            MasterName = masterName,
            ViewData = base.ViewData,
            TempData = base.TempData,
            ViewEngineCollection = this.ViewEngineCollection
        };
    }
    protected internal ViewResult View(IView view)
    {
        return this.View(view, null);
    }
    protected internal virtual ViewResult View(IView view, object model)
    {
        if (model != null)
        {
            base.ViewData.Model = model;
        }
        return new ViewResult
        {
            View = view,
            ViewData = base.ViewData,
            TempData = base.TempData
        };
    }
}
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的呈現二》

相關文章
相關標籤/搜索