2014-03-26 36 views
5

我避免了重定向錯誤的默認ASP.NET方法(很多人都這麼做)。清理AJAX代碼和SEO是其中的原因。通過responseMode =「ExecuteURL」清除HttpContext.Current.Items?

但是,我正在使用以下方法來執行此操作,而且似乎在傳輸中可能會丟失HttpContext.Current.Items

<httpErrors errorMode="Custom" existingResponse="Replace"> 
    <remove statusCode="401" /> 
    <remove statusCode="403" /> 
    <remove statusCode="404" /> 
    <remove statusCode="500" /> 
    <error statusCode="401" responseMode="ExecuteURL" path="/Account/SignIn" /> 
    <error statusCode="403" responseMode="ExecuteURL" path="/Site/Forbidden" /> 
    <error statusCode="404" responseMode="ExecuteURL" path="/Site/NotFound" /> 
    <error statusCode="500" responseMode="ExecuteURL" path="/Site/Error" /> 
</httpErrors> 

我以爲這只是蓋子,我的理解保留Items下進行Server.Transfer()。 (參見:Scope of HttpContext.Current.Itemshttp://weblog.west-wind.com/posts/2010/Jan/20/HttpContextItems-and-ServerTransferExecute

但我還捕捉了「ExecuteURL」前Items的東西,和檢索/轉讓後其輸出(或者不管它是什麼),它似乎消失了。我看過它進入Items集合,我看到Count增加到5,然後當檢索到值時,集合中只有2個項目。

這是怎麼回事?


如果您想了解更多關於我在做什麼,並建議替代的實現,我接受它。我使用這種方式將ELMAH錯誤ID以不受競態條件限制的方式推送到ViewModel中。 (即這是我更換是:只顯示最近的錯誤一個共同的解決辦法),這裏是我的代碼:

的Global.asax

protected void ErrorLog_Logged(object sender, ErrorLoggedEventArgs args) { 
    ElmahSupplement.CurrentId = args.Entry.Id; 
} 

void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e) { 
    if (ElmahSupplement.IsNotFound(e.Exception)) { 
     ElmahSupplement.LogNotFound((e.Context as HttpContext).Request); 
     e.Dismiss(); 
    } 
} 

SiteController.cs

public virtual ActionResult Error() { 
    Response.StatusCode = 500; 
    return View(MVC.Site.Views.Error, ElmahSupplement.CurrentId); 
} 

ElmahSupplement.cs

public class ElmahSupplement { 
    // TODO: This is a rather fragile way to access this info 
    private static readonly Guid contextId = new Guid("A41A67AA-8966-4205-B6C1-14128A653F21"); 

    public static string CurrentId { 
     get { 
      return 
       // Elmah 1.2 will fail to log when enumerating form values that raise RequestValidationException (angle brackets) 
       // https://code.google.com/p/elmah/issues/detail?id=217 
       // So this id could technically be empty here 
       (HttpContext.Current.Items[contextId] as string); 
     } 
     set { 
      HttpContext.Current.Items[contextId] = value; 
     } 
    } 

    public static void LogNotFound(HttpRequest request) { 
     var context = RepositoryProxy.Context; 
     context.NotFoundErrors.Add(new NotFoundError { 
      RecordedOn = DateTime.UtcNow, 
      Url = request.Url.ToString(), 
      ClientAddress = request.UserHostAddress, 
      Referrer = request.UrlReferrer == null ? "" : request.UrlReferrer.ToString() 
     }); 
     context.SaveChanges(); 
    } 

    public static bool IsNotFound(Exception e) { 
     HttpException he = e as HttpException; 
     return he != null && he.GetHttpCode() == 404; 
    } 
} 
+1

您的假設是錯誤的,ASP.NET MVC不再使用Server.Transfer,因此它的行爲並不像您期望的那樣。而Server.Transfer也不能用於ASP.NET MVC,因爲MVC是在異步管道上重建的。 –

+0

阿卡什,你似乎在做兩個評論,但我理解他們說同樣的話,所以也許我誤解了?另外,雖然我當然相信你,但我並不真正瞭解Server.Transfer的問題。這不是在MVC處理程序的範圍之外執行的嗎?我想最重要的是,如果你有關於如何解決的建議,請做出來。 18小時直到失去+50。 ( – shannon

回答

0

我跟蹤了一下,並確定了以下內容。有些是鬆散推斷的。

CustomErrorModule(位於IIS模塊堆棧中)收到SEND_RESPONSE通知。

HttpStatus爲500,因此它會克隆上下文,設置一個新URL(根據匹配的自定義錯誤規則),並在此上下文中執行請求(請參閱ExecuteRequest)。

HttpContext.Items每文檔的目的是:

獲取一個密鑰/值集合可用於HTTP請求期間,組織和共享IHttpModule的接口和IHttpHandler接口 之間 數據。

關鍵看這個函數定義,當然只有「HTTP請求」。然而,Items字典本身似乎是一個字典中的項目,該字典在HttpContext上鍵入,該HttpContext是此執行子請求中的唯一(克隆)引用。跟蹤顯示爲此ExecuteURL運行的完整管道(所有模塊,例如重複驗證),所以這個獨立的上下文當然是必需的。

從非託管代碼,它是微不足道的GetParentContext。但是,從託管代碼中,此層次結構不可用。所以,我沒有辦法檢索原始的Items

作爲一種替代解決方案,它可能會利用Global.asax變量的功能,因爲我的測試顯示子請求共享ApplicationInstance,但我不確定客戶端對此的訪問必然是順序的。另一種可能更好的方法是避免重新運行整個流水線;永不退出MVC處理程序(例如Controller.OnExceptionTransferToAction)。但是,這可以防止爲錯誤頁面配置實現單點實例,因爲在MVC意識之外也可能引發錯誤。

1

如解釋here所示,ExecuteURL生成兩個請求:第一個請求引發異常,第二個請求產生錯誤響應。

由於在請求之間清除了Context.Items,因此您的代碼始終會看到生成的第二個請求,因此會看到Items之間的差異。

嘗試在帖子中進行消解:改爲使用system.web> customErrors和redirectMode =「ResponseRewrite」。

+0

)恐怕沒有回答爲什麼上下文會被清除,如果實際上有第二個'虛擬'客戶端請求被實現,機制是什麼?所以我仍然沒有足夠的信息來解決它,如果建議的解決方法適用,d仍然接受答案,但它不適用於MVC。感謝您通過鏈接(+1)。我會看看跟蹤。 – shannon

+0

我應該澄清,由於主要上下文'Items'的功能是在處理程序和模塊之間共享信息,第二個請求必須在流程的開始時自動啓動,以自然地丟失該信息('HttpContext')。同時,IP響應 – shannon

+0

從我的測試中,似乎CustomErrorModule的ExecuteURL生成一個在克隆的HttpContext上運行的子請求,它運行整個管道,由前一個上下文包裝,但是由於這個子上下文被克隆在unmanag ed編碼它不會收到'Items'的重複。 – shannon