5

我有一個HttpModule,我通過將幾個不同的源代碼聯機在一起放到一起(大部分)與傳統ASP.NET應用程序一起使用,以及ASP.NET MVC應用。其中最大的部分來自CodePlex上的kigg項目。我的問題是在處理404錯誤,由於缺少圖像。在下面的代碼中,我不得不通過HttpContext的Request對象中的AcceptedTypes集合顯式查找請求的圖像。如果我沒有放入此檢查,即使是丟失的圖像也會導致重定向到Web.config中我的部分中定義的404頁面。用於錯誤處理和丟失圖像的HttpModule

這種方法的問題是(超出它聞起來的事實)是,這只是圖像。我基本上必須做到這一點,每一個可以想象的內容類型,我不希望這種重定向行爲發生。

看看下面的代碼,有人可以推薦某種重構,可以允許它對非頁面請求更寬鬆嗎?我仍然希望他們在IIS日誌中(所以我可能不得不刪除ClearError()調用),但我認爲破碎的圖像不應該影響用戶體驗以將它們重定向到錯誤頁面。

的代碼如下:

/// <summary> 
/// Provides a standardized mechanism for handling exceptions within a web application. 
/// </summary> 
public class ErrorHandlerModule : IHttpModule 
{ 
    #region Public Methods 

    /// <summary> 
    /// Disposes of the resources (other than memory) used by the module that implements 
    /// <see cref="T:System.Web.IHttpModule"/>. 
    /// </summary> 
    public void Dispose() 
    { 
    } 

    /// <summary> 
    /// Initializes a module and prepares it to handle requests. 
    /// </summary> 
    /// <param name="context"> 
    /// An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events 
    /// common to all application objects within an ASP.NET application.</param> 
    public void Init(HttpApplication context) 
    { 
     context.Error += this.OnError; 
    } 

    #endregion 

    /// <summary> 
    /// Called when an error occurs within the application. 
    /// </summary> 
    /// <param name="source">The source.</param> 
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> 
    private void OnError(object source, EventArgs e) 
    { 
     var httpContext = HttpContext.Current; 

     var imageRequestTypes = 
      httpContext.Request.AcceptTypes.Where(a => a.StartsWith("image/")).Select(a => a.Count()); 

     if (imageRequestTypes.Count() > 0) 
     { 
      httpContext.ClearError(); 
      return; 
     } 

     var lastException = HttpContext.Current.Server.GetLastError().GetBaseException(); 
     var httpException = lastException as HttpException; 
     var statusCode = (int)HttpStatusCode.InternalServerError; 

     if (httpException != null) 
     { 
      statusCode = httpException.GetHttpCode(); 
      if ((statusCode != (int)HttpStatusCode.NotFound) && (statusCode != (int)HttpStatusCode.ServiceUnavailable)) 
      { 
       // TODO: Log exception from here. 
      } 
     } 

     var redirectUrl = string.Empty; 

     if (httpContext.IsCustomErrorEnabled) 
     { 
      var errorsSection = WebConfigurationManager.GetSection("system.web/customErrors") as CustomErrorsSection; 
      if (errorsSection != null) 
      { 
       redirectUrl = errorsSection.DefaultRedirect; 

       if (httpException != null && errorsSection.Errors.Count > 0) 
       { 
        var item = errorsSection.Errors[statusCode.ToString()]; 

        if (item != null) 
        { 
         redirectUrl = item.Redirect; 
        } 
       } 
      } 
     } 

     httpContext.Response.Clear(); 
     httpContext.Response.StatusCode = statusCode; 
     httpContext.Response.TrySkipIisCustomErrors = true; 
     httpContext.ClearError(); 

     if (!string.IsNullOrEmpty(redirectUrl)) 
     { 
      var mvcHandler = httpContext.CurrentHandler as MvcHandler; 
      if (mvcHandler == null) 
      { 
       httpContext.Server.Transfer(redirectUrl);      
      } 
      else 
      { 
       var uriBuilder = new UriBuilder(
        httpContext.Request.Url.Scheme, 
        httpContext.Request.Url.Host, 
        httpContext.Request.Url.Port, 
        httpContext.Request.ApplicationPath); 

       uriBuilder.Path += redirectUrl; 

       string path = httpContext.Server.UrlDecode(uriBuilder.Uri.PathAndQuery); 
       HttpContext.Current.RewritePath(path, false); 
       IHttpHandler httpHandler = new MvcHttpHandler(); 

       httpHandler.ProcessRequest(HttpContext.Current); 
      } 
     } 
    } 
} 

任何反饋,將不勝感激。我目前正在做這個的應用程序是一個ASP.NET MVC應用程序,但正如我所說,它被編寫爲與MVC處理程序一起工作,但僅當CurrentHandler屬於該類型時才適用。

編輯:我忘了提及的「黑客」,在這種情況下將是的OnError(以下線):

 var imageRequestTypes = 
     httpContext.Request.AcceptTypes.Where(a => a.StartsWith("image/")).Select(a => a.Count()); 

    if (imageRequestTypes.Count() > 0) 
    { 
     httpContext.ClearError(); 
     return; 
    } 
+0

而不是建立自己的錯誤存入模塊,您是否考慮過使用其中一個現有的錯誤日誌記錄庫,例如ELMAH(http://code.google.com/p/elmah/)或ASP.NET Health Monitoring(http://msdn.microsoft.com/ EN-US /庫/ ms998306.aspx)? ELMAH有一個豐富的錯誤過濾API,如果需要,您可以在Web.config中或通過代碼聲明性地指定它。 – 2009-10-10 23:27:23

+0

斯科特,我絕對考慮過它,並在過去使用過ELMAH。這比其他任何事都更像是一種編碼練習。 – 2009-10-23 16:51:55

回答

5

最終,問題是由傳統的ASP.NET應用程序和ASP.NET MVC應用程序提供的不同類型的Context區分造成的。通過提供檢查來確定我正在處理的上下文類型,我能夠做出相應的迴應。

我已經爲HttpTransfer和MvcTransfer添加了單獨的方法,允許我重定向到錯誤頁面,特別是在需要時。我還改變了邏輯,以便我的本地和開發機器上可以輕鬆獲取YSOD,而不需要處理程序吞下異常。

隨着用於異常登錄到數據庫(由TODO註釋表示)代碼之外,我們使用的是最後的代碼是:

using System; 
using System.Net; 
using System.Security.Principal; 
using System.Web; 
using System.Web.Configuration; 
using System.Web.Mvc; 

using Diagnostics; 

/// <summary> 
/// Provides a standardized mechanism for handling exceptions within a web application. 
/// </summary> 
public sealed class ErrorHandlerModule : IHttpModule 
{ 
    #region Public Methods 

    /// <summary> 
    /// Disposes of the resources (other than memory) used by the module that implements 
    /// <see cref="T:System.Web.IHttpModule"/>. 
    /// </summary> 
    public void Dispose() 
    { 
    } 

    /// <summary> 
    /// Initializes a module and prepares it to handle requests. 
    /// </summary> 
    /// <param name="context"> 
    /// An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events 
    /// common to all application objects within an ASP.NET application.</param> 
    public void Init(HttpApplication context) 
    { 
     context.Error += OnError; 
    } 

    #endregion 

    #region Private Static Methods 

    /// <summary> 
    /// Performs a Transfer for an MVC request. 
    /// </summary> 
    /// <param name="url">The URL to transfer to.</param> 
    /// <param name="currentContext">The current context.</param> 
    private static void HttpTransfer(string url, HttpContext currentContext) 
    { 
     currentContext.Server.TransferRequest(url); 
    } 

    /// <summary> 
    /// Performs a Transfer for an MVC request. 
    /// </summary> 
    /// <param name="url">The URL to transfer to.</param> 
    /// <param name="currentContext">The current context.</param> 
    private static void MvcTransfer(string url, HttpContext currentContext) 
    { 
     var uriBuilder = new UriBuilder(
      currentContext.Request.Url.Scheme, 
      currentContext.Request.Url.Host, 
      currentContext.Request.Url.Port, 
      currentContext.Request.ApplicationPath); 

     uriBuilder.Path += url; 

     string path = currentContext.Server.UrlDecode(uriBuilder.Uri.PathAndQuery); 
     HttpContext.Current.RewritePath(path, false); 
     IHttpHandler httpHandler = new MvcHttpHandler(); 

     httpHandler.ProcessRequest(HttpContext.Current); 
    } 

    #endregion 

    #region Private Methods 

    /// <summary> 
    /// Called when an error occurs within the application. 
    /// </summary> 
    /// <param name="source">The source.</param> 
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param> 
    private static void OnError(object source, EventArgs e) 
    { 
     var httpContext = HttpContext.Current; 
     var lastException = HttpContext.Current.Server.GetLastError().GetBaseException(); 
     var httpException = lastException as HttpException; 
     var statusCode = (int)HttpStatusCode.InternalServerError; 

     if (httpException != null) 
     { 
      if (httpException.Message == "File does not exist.") 
      { 
       httpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; 
       httpContext.ClearError(); 
       return; 
      } 

      statusCode = httpException.GetHttpCode(); 
     } 

     if ((statusCode != (int)HttpStatusCode.NotFound) && (statusCode != (int)HttpStatusCode.ServiceUnavailable)) 
     { 
      // TODO : Your error logging code here. 
     } 

     var redirectUrl = string.Empty; 

     if (!httpContext.IsCustomErrorEnabled) 
     { 
      return; 
     } 

     var errorsSection = WebConfigurationManager.GetSection("system.web/customErrors") as CustomErrorsSection; 
     if (errorsSection != null) 
     { 
      redirectUrl = errorsSection.DefaultRedirect; 

      if (httpException != null && errorsSection.Errors.Count > 0) 
      { 
       var item = errorsSection.Errors[statusCode.ToString()]; 

       if (item != null) 
       { 
        redirectUrl = item.Redirect; 
       } 
      } 
     } 

     httpContext.Response.Clear(); 
     httpContext.Response.StatusCode = statusCode; 
     httpContext.Response.TrySkipIisCustomErrors = true; 
     httpContext.ClearError(); 

     if (!string.IsNullOrEmpty(redirectUrl)) 
     { 
      var mvcHandler = httpContext.CurrentHandler as MvcHandler; 
      if (mvcHandler == null) 
      { 
       try 
       { 
        HttpTransfer(redirectUrl, httpContext); 
       } 
       catch (InvalidOperationException) 
       { 
        MvcTransfer(redirectUrl, httpContext); 
       } 
      } 
      else 
      { 
       MvcTransfer(redirectUrl, httpContext); 
      } 
     } 
    } 

    #endregion 
} 
0

你爲什麼不趕在Global.asax中404?

protected void Application_Error(object sender, EventArgs args) { 

    var ex = Server.GetLastError() as HttpException; 
    if (ex != null && ex.ErrorCode == -2147467259) { 

    } 
} 
+0

該模塊的要點是「一次寫入」解決方案,這樣我就不必在Global.asax中放入臨時代碼。我想我可能會修改正在工作的HttpModule。我要慢慢煨一下,然後運行一些測試,以便mak – 2009-09-20 23:20:58

+0

er ...提交得太快了。如果我沒有看到任何問題,我會稍微煨一下,然後運行一些測試,以確定它將在明天停止並在明天發佈。 – 2009-09-20 23:21:51

+0

處理圖像或其他靜態內容的404時,不會查詢Global.asax。 – DanO 2012-05-30 22:21:46

0

如果我理解正確,您只想處理導致404的操作的錯誤?

您可以檢查請求的路由是否爲空或停止路由 - 這實質上是路由處理程序如何工作以決定請求是否應該繼續進入mvc管道。

var iHttpContext = new HttpContextWrapper(httpContext); 
var routeData = RouteTable.Routes.GetRouteData(iHttpContext); 
if(routeData == null || routeData.RouteHandler is StopRoute) 
{ 
    // This is a route that would not normally be handled by the MVC pipeline 
    httpContext.ClearError(); 
    return; 
} 

順便說一句,重定向由於404導致小於理想的用戶體驗,並且是從ASP.NET宿醉(你不能觀看處理從請求處理分開)。管理404的正確方法是向瀏覽器返回一個404狀態代碼,並顯示您的自定義錯誤頁面而不是重定向(這會導致302狀態代碼發送到瀏覽器)。