在MVC里面使用Response.Redirect方法后記得返回EmptyResult


在ASP.NET MVC中我們很多時候都會在攔截器和Controller中直接使用Response.Redirect方法做跳轉,但是實際上Response.Redirect方法執行后ASP.NET並不會立即結束當前請求的執行,而是要過一段時間才會終止當前請求的執行,然后命令客戶端瀏覽器去訪問Response.Redirect方法中傳入的新的URL地址。這會導致一個問題,有時候我們希望Response.Redirect方法執行后后面的代碼就取消執行了,因為這並不是我們預期的行為,當代碼執行了Response.Redirect方法后,如果其后面的部分代碼還繼續在執行甚至有可能報錯。

 

比如在下面的代碼中我們演示了在MVC的IAuthorizationFilter攔截器中,如果用戶沒有登錄應該立刻停止當前頁面的請求,然后跳轉到登錄頁面。我們使用了Response.Redirect方法來做跳轉。

 1 public class AuthenticationFilterAttribute:ActionFilterAttribute,IAuthorizationFilter,IActionFilter
 2     {
 3         #region IAuthorizationFilter Members
 4 
 5         public void OnAuthorization(AuthorizationContext filterContext)
 6         {
 7 
 8             string curActionName = filterContext.ActionDescriptor.ActionName;
 9             string curControllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
10 
11             if (!filterContext.HttpContext.Request.IsAuthenticated && (curControllerName.ToLower() != "login" || curActionName.ToLower() != "index"))
12             {
13                 filterContext.HttpContext.Response.Redirect(LoginHelper.LoginPageUrl, true);
14                 return;
15             }
16         }
17 
18         #endregion
19     }

結果我驚訝的發現即便代碼在上面13行Response.Redirect了,ASP.NET還是執行了當前請求URL對應Controller的Action中的代碼,甚至執行了Action返回的View的Razor引擎代碼。。。雖然最終這個view的內容沒有呈現給客戶端瀏覽器,瀏覽器最后還是正確跳轉到了登錄頁。但是根據調試我們發現即便是我們執行了Response.Redirect方法,ASP.NET之后還是執行了一大堆本不該執行的代碼

 

所以這個時候我們需要用到EmptyResult這個對象,我們將上面的代碼改成如下所示:

 1 public class AuthenticationFilterAttribute:ActionFilterAttribute,IAuthorizationFilter,IActionFilter
 2     {
 3         #region IAuthorizationFilter Members
 4 
 5         public void OnAuthorization(AuthorizationContext filterContext)
 6         {
 7 
 8             string curActionName = filterContext.ActionDescriptor.ActionName;
 9             string curControllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
10 
11             if (!filterContext.HttpContext.Request.IsAuthenticated && (curControllerName.ToLower() != "login" || curActionName.ToLower() != "index"))
12             {
13                 filterContext.HttpContext.Response.Redirect(LoginHelper.LoginPageUrl, true);
14                 filterContext.Result = new EmptyResult();//加入EmptyResult就告訴ASP.NET MVC在本攔截器執行結束后,不必再為當前請求執行Controller中Action的代碼
15                 return;
16             }
17         }
18 
19         #endregion
20     }

那么ASP.NET MVC在執行完上面的IAuthorizationFilter攔截器后,就會發現EmptyResult被賦值在了參數filterContext的Result屬性上,就會終止執行Controller的Action,立即結束當前請求的執行,不會再去執行多余的代碼了。其實filterContext的Result屬性只要在IAuthorizationFilter攔截器中被賦值了不為null,就不會執行Controller的Action了,同時在該IAuthorizationFilter攔截器之后注冊的其它Filter攔截器也都不會被執行了。

 

比如在ASP.NET Core MVC(由於在ASP.NET MVC中JsonResult的構造函數不帶參數,所以在攔截器中使用JsonResult的意義不大,不如就用EmptyResult)中的IAuthorizationFilter攔截器中,我們也可以選擇給context的Result屬性賦值為JsonResult:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;

namespace WebApi.Filters
{
    public class AuthorizationFilterAttribute : Attribute, IAuthorizationFilter
    {
        public AuthorizationFilterAttribute()
        {

        }

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            context.Result = new JsonResult(new { message = "Http請求被AuthorizationFilter攔截" });//加入JsonResult來告訴ASP.NET Core MVC在本攔截器執行結束后,不必再為當前請求執行Controller中Action的代碼,同時取消執行在本攔截器之后注冊的其它Filter攔截器,此外會通過Http的Response返回一個{"message":"Http請求被AuthorizationFilter攔截"}的Json對象到客戶端瀏覽器
        }
    }
}

只不過這樣會同時返回一個Json對象給客戶端瀏覽器。

 

在攔截器中,還可以結合設置StatusCode和EmptyResult,來返回特定狀態碼的Http響應,例如下面我們就展示了如何在IAuthorizationFilter攔截器中,返回401狀態的Http響應

using System;
using System.IO;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace WebApi.Filters
{
    public class AuthorizationFilterAttribute : Attribute, IAuthorizationFilter
    {
        public AuthorizationFilterAttribute()
        {

        }

        /// <summary>
        /// 設置StatusCode和EmptyResult,返回401狀態的Http響應,並發送一段html為Http響應體
        /// </summary>
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            context.HttpContext.Response.ContentType = "text/html; charset=utf-8";//設置Http響應類型為text/html,編碼為utf-8
            context.HttpContext.Response.StatusCode = 401;//設置Http響應狀態碼為401

            using (StreamWriter sw = new StreamWriter(context.HttpContext.Response.Body, Encoding.UTF8))
            {
                sw.Write("<html><head></head><body><h1>Unauthorized!</h1></body></html>");
            }

            context.Result = new EmptyResult();//加入EmptyResult就告訴ASP.NET Core MVC在本攔截器執行結束后,不必再為當前請求執行Controller中Action的代碼,同時取消執行在本攔截器之后注冊的其它Filter攔截器
        }

        /// <summary>
        /// 設置UnauthorizedResult,返回401狀態的Http響應
        /// </summary>
        //public void OnAuthorization(AuthorizationFilterContext context)
        //{
        //    context.Result = new UnauthorizedResult();//加入UnauthorizedResult就告訴ASP.NET Core MVC在本攔截器執行結束后,不必再為當前請求執行Controller中Action的代碼,同時取消執行在本攔截器之后注冊的其它Filter攔截器,並且返回401狀態的Http響應
        //}
    }
}

 

所以很多時候我們在ASP.NET中使用Response.Redirect方法時要相當小心,一定要知道Response.Redirect方法執行后並不等於代碼就結束執行了,還要考慮到后面的代碼一旦被誤執行會有什么后果,有什么辦法可以避免。在MVC中使用EmptyResult就是個不錯的選擇。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM