程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> ASP.NET >> 關於ASP.NET >> Response.Redirect引起的“無法在發送HTTP標頭之後進行重定向”

Response.Redirect引起的“無法在發送HTTP標頭之後進行重定向”

編輯:關於ASP.NET

博客後台切換至i.cnblogs.com之後,在日志中發現大量的“無法在發送HTTP標頭之後進行重定向”(Cannot redirect after HTTP headers have been sent)的錯誤信息。

檢查代碼發現問題是由下面的代碼觸發的:

IHttpHandler IHttpHandlerFactory.GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
    context.Response.Redirect("http://i.cnblogs.com/" + 
        context.Request.RawUrl.Substring(context.Request.RawUrl.LastIndexOf("/") + 1));
    
    //後續也有context.Response.Redirect代碼
    //...
    return PageParser.GetCompiledPageInstance(newurl, path, context);
}

“無法在發送HTTP標頭之後進行重定向”問題來源於Response.Redirect之後,又進行了Response.Redirect。

解決方法很簡單:在Response.Redirect之後立即返回。

IHttpHandler IHttpHandlerFactory.GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
    context.Response.Redirect("http://i.cnblogs.com/" + 
        context.Request.RawUrl.Substring(context.Request.RawUrl.LastIndexOf("/") + 1));
    return null;
    //...    
}

為什麼之前沒有加return null呢?因為以前一直以為Response.Redirect會結束當前請求,不會執行Response.Redirect之後的代碼。

現在殘酷的現實說明了不完全是這樣的,那問題背後的真相是什麼?讓我們來一探究竟。

由於微軟公開了.NET Framework的源代碼,現在無需再看Reflactor出來的代碼,可以直接下載源代碼用Visual Studio進行查看。

.NET Framework源代碼下載鏈接:http://referencesource.microsoft.com/download.html (相關新聞:微軟開放了.NET 4.5.1的源代碼)

用Visual Studio打開DotNetReferenceSource\Source\ndp.sln,搜索HttpResponse.cs,找到Response.Redirect的實現代碼:

public void Redirect(String url)
{
    Redirect(url, true, false);
}

實際調用的是internal void Redirect(String url, bool endResponse, bool permanent) ,傳給endResponse的值的確是true啊,為什麼後面的代碼還會執行?

進一步查看internal void Redirect()的實現代碼(省略了無關代碼):

internal void Redirect(String url, bool endResponse, bool permanent) 
{
    //...
    
    Page page = _context.Handler as Page;
    if ((page != null) && page.IsCallback) {
        //拋異常
    }
    
    // ... url處理
    
    Clear(); //Clears all headers and content output from the buffer stream.
    
    //...
    this.StatusCode = permanent ? 301 : 302; //進行重定向操作
    //...
    _isRequestBeingRedirected = true; 
    
    var redirectingHandler = Redirecting;
    if (redirectingHandler != null) {
        redirectingHandler(this, EventArgs.Empty);
    }
    
    if (endResponse)
        End(); //結束當前請求
}

從上面的代碼可以看出,我們要找的真相在End()方法中,繼續看HttpResponse.End()的實現代碼:

public void End() {
    if (_context.IsInCancellablePeriod) {
        AbortCurrentThread();
    }
    else {
        // when cannot abort execution, flush and supress further output
        _endRequiresObservation = true;
    
        if (!_flushing) { // ignore Reponse.End while flushing (in OnPreSendHeaders)
            Flush();
            _ended = true;
    
            if (_context.ApplicationInstance != null) {
                _context.ApplicationInstance.CompleteRequest();
            }
        }
    }
}

注意啦!真相浮現了!

以前一直以為的Response.Redirect會結束當前請求,就是上面的AbortCurrentThread()情況,如果將Response.Redirect放在try...catch中就會捕捉到ThreadAbortException異常。

通常情況下,我們在WebForms的Page或MVC的Controller中進行Redirect,_context.IsInCancellablePeriod的值為true,執行的是AbortCurrentThread(),所以不會遇到這個問題。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved