5

私はHttpModuleを持っています。これは、いくつかの異なるソースをオンラインで一緒にcobblingして、(ほとんど)従来の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/)のような既存のエラーログライブラリの使用を考慮していますか? ja-jp/library/ms998306.aspx)? ELMAHには、必要に応じてWeb.configやコードで宣言的に指定できる豊富なエラーフィルタリングAPIがあります。 –

+0

スコット、私は間違いなくそれを考慮し、ELMAHを過去に使用しました。これは何よりもコーディングのエクササイズであった。 –

答えて

5

最終的には、従来のASP.NETアプリケーションとASP.NET MVCアプリケーションで提供されるさまざまな種類のコンテキストを区別しないことによって問題が発生していました。私が扱っていた文脈のタイプを決定するためのチェックを提供することによって、私はそれに応じて対応することができました。

私はエラーページに、特に必要なときにリダイレクトできるように、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を変更した可能性があると思います。私はちょっと煮詰めて、いくつかのテストをして、マックにするつもりです –

+0

...あまりにも早く提出されました。私はちょっと試してみて、いくつかのテストをして、何か問題がなければ明日ここに戻って嗅ぎ取ることになっていることを確かめます。 –

+0

イメージやその他の静的コンテンツに対して404を処理する場合、Global.asaxは参照されません。 – DanO

0

私が正しく理解していれば、404という結果になるアクションのエラーを処理したいだけですか?

リクエストのルートがヌルであるか、またはルーティングが停止されているかどうかを確認できます。これは、リクエストがmvcパイプラインに継続するかどうかを判断するためにURLルーティングハンドラがどのように動作するかです。

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ステータスコードが送信されます)。

関連する問題