10

私が使用していたMVC3Webアプリケーションで

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
}

未処理の例外が発生した場合にユーザーに「エラー」ビューが表示されるグローバルエラー処理を適用します。

ある特定のビューでは、メソッドを。で修飾することによって未処理の例外が発生した場合に、別のエラービューを表示することも必要 [HandleError(View = "SpecialError")]でした。これはうまくいきました。

次に、未処理の例外のグローバルロギングを追加したいと思いました。ロギングコードを使用してカスタムHandleError属性を作成しました。

public class MyHandleErrorAttribute : HandleErrorAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            // Write to log code
            base.OnException(context);
        }
    }

また、RegisterGlobalFiltersとメソッドの装飾を更新して、代わりにこの属性名を使用するようにしました。MyHandleError(View = "SpecialError")]これは一般的に機能しますが、OnExceptionメソッドで装飾されたメソッド内で例外が発生すると、 2回呼び出されます。もともと、この属性でメソッドを装飾することはグローバルハンドラーに取って代わったと思いましたが、それは単に追加されたようです(これはより理にかなっていますが、私が望むものではありません)。OnExceptionを2回呼び出すことにより、同じ例外が2回ログに記録されますが、これは発生してはなりません。OnExceptionはカスタム属性であるため、 2回呼び出されているとは思いません。これは、標準のHandleError属性でも発生すると思います。これは、レコードを作成しているときに表示されるだけです。

最終的には、[HandleError]によって提供される機能、特に特定のメソッド例外に異なるビューを設定しながら、すべての未処理の例外を(1回)ログに記録したいと思います。これを行うためのクリーンな方法はありますか?

4

4 に答える 4

9

私はこれに対するクリーンな解決策を自分で見つけたと信じています。HandleErrorを拡張することは良い考えのように思えましたが、今ではそれは間違った方向への一歩だったと思います。エラーを別の方法で処理したくはありませんでした。HandleErrorがエラーを取得する前に、ログに例外を1回書き込むだけです。このため、デフォルトのHandleErrorはそのままにしておくことができます。OnExceptionは複数回呼び出すことができますが、HandleErrorAttributeの標準実装では完全に無害であるように見えます。

代わりに、例外ログフィルターを作成しました。

public class LoggedExceptionFilter : IExceptionFilter
    {
        public void OnException(ExceptionContext filterContext)
        {
            // logging code
        }
    }

FilterAttributeHandleErrorAttributeと一緒にRegisterGlobalFilters内​​に一度登録されるだけなので、から継承する必要はありません。

 public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new LoggedExceptionFilter());
        filters.Add(new HandleErrorAttribute());
    }

[HandleError]これにより、標準機能を変更せずに例外を適切にログに記録できます。

于 2012-07-14T00:24:39.460 に答える
3

これを試して、

public class MyHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
         var exceptionHandled = context.ExceptionHandled;

         base.OnException(context);                          

         if(!exceptionHandled && context.ExceptionHandled)
           // log the error.
    }
}
于 2012-07-12T11:41:38.943 に答える
2

IFilterProviderフィルタがそのアクションにすでに適用されているかどうかを確認するカスタムを作成できます。

public class MyFilterProvider : IFilterProvider
{
    public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        if (!actionDescriptor.GetFilterAttributes(true).Any(a => a.GetType() == typeof(MyHandleErrorAttribute)))
        {
            yield return new Filter(new MyHandleErrorAttribute(), FilterScope.Global, null);
        }
    }
}

次に、フィルターをに登録する代わりにGlobalFilterCollection、フィルタープロバイダーをに登録します。 Application_Start()

FilterProviders.Providers.Add(new MyFilterProvider());

またはExceptionHandled(@Markが提案したものと同様に)、のプロパティを明示的に設定できます。ExceptionContext

public class MyHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        if(context.ExceptionHandled) return;

        // Write to log code
        base.OnException(context);
        context.ExceptionHandled = true;
    }
}
于 2012-07-12T11:41:29.223 に答える
-1

私は実際に、OnExceptionメソッドが2回起動しないようにするための解決策を見つけました。FilterConfig.RegisterGlobalFilters()メソッドを使用している場合は、HandleErrorAttributeの登録をコメントアウトします。

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //filters.Add(new HandleErrorAttribute());
    }
}

実際、組み込みのHandleErrorAttributeも登録せずに使用しましたが、正常に機能しました。カスタムエラーをオンにするだけで済みます。

 <system.web>
    <customErrors mode="On" />
 </system.web>
于 2013-05-23T23:35:08.780 に答える