14

MVC4 (RC) アプリケーションで奇妙な問題に遭遇しました。(.NET 4.0 で実行)

例外/エラーをログに記録するために Elmah をセットアップしました。

基本的に、Elmah.MVCelmah.sqlserver NuGet パッケージをインストールしました。(それぞれバージョン 2.0.0 および 1.2)

箱から出して問題なく動作するように見えました-elmahページにアクセスしてエラーを表示できます:

http://myserver/elmah

たとえば、404 エラーを作成すると、このログに表示されます。

機能していないのは次のとおりです。アクションを備えた標準の MVC コントローラーがあり[HttpPost]ます。常に例外をスローするように設定しました:

public class TestController : Controller
{
    [HttpPost]
    [ValidateInput(false)]
    public void Testing()
    {
        throw new Exception("uh oh");
    }
}

次に、jQuery を介してこのコントローラーにデータを投稿しようとします。

$.post('/Test/Testing', {test_data: 'This is some test data'});

わかりました、これはうまくいきます。応答は典型的な死の黄色の画面を返し、エラーがキャッチされて Elmah に記録されます。

ただし、XML/HTML のようなものを投稿しようとすると、エラーはElmah に記録されません。サーバーからはまだ同じ応答が返されますが (黄色の死の画面)、Elmah には何も返されません。

$.post('/Test/Testing', {test_data: '<test><test1>This is some test data</test1></test>'});

なんで?意味がありません。

アクションのリクエスト検証をすでにオフにしていることに注意してください。そうしないと、XML/HTML データを投稿すると、次の例外が発生します。

潜在的に危険な Request.Form 値がクライアントから検出されました

NuGet もその例外をログに記録することを拒否します-これはバグだと思います:

http://code.google.com/p/elmah/issues/detail?id=217

では、私が経験しているこの問題の原因は何ですか? 上記で見つけた問題に関連するバグですか?

リクエストに XML/HTML が含まれているという理由だけで例外をログに記録できないのは、非常に残念な状況のようです。

確かにこれを回避する方法はありますか?

4

7 に答える 7

4

ELMAH はデフォルトで HttpRequestValidationException をキャッチしません。ユーザーが無効なリクエストを送信すると、ELMAH のレポートでは見逃されます。したがって、このグローバル フィルターも定義して使用する必要があります。

public class ElmahRequestValidationErrorFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        if (context.Exception is HttpRequestValidationException)
           ErrorLog.GetDefault(HttpContext.Current).Log(new Error(context.Exception));
    }
}
于 2014-01-01T10:48:46.210 に答える
2

誰かがhttp://code.google.com/p/elmah/issues/detail?id=217で提案した回避策があります

<system.web>のセクションにこれを追加することで、ASP に古い要求検証ロジックを強制的に使用させることができますWeb.config

<httpRuntime requestValidationMode="2.0" />

これはアプリ全体に影響を与えますが、それほど大きな状況ではありません。

誰かもっといいのあったら教えてください。

于 2012-07-24T12:24:32.383 に答える
1

これは、現在のリリース (この記事の執筆時点) 1.2.2 の後に修正されたようです。ログにエラーを明示的に記録することになりました。これは、未処理の例外 (電子メール通知など) として通常の処理を通過しませんが、エラー ログに記録されます。

catch (Exception ex)
{
    var newEx = new HttpException(500, "Kaboom!!!", ex);

    // Adding this explicit ELMAH logging because of ELMAH bug:
    // https://code.google.com/p/elmah/issues/detail?id=217
    // Waiting for a new ELMAH release with the fix
    var elmahError = new Elmah.Error(newEx);
    var elmahLog = Elmah.ErrorLog.GetDefault(HttpContext.ApplicationInstance.Context);
    elmahLog.Log(elmahError);

    throw newEx;
}
于 2014-08-07T15:09:49.943 に答える
1

しばらく前に修正された Elmah のバグがありましたが、Google コード サイトのどのビルドもそれを含めるのに十分なほど最近のものではないと思います。

コメントで述べたように、モデル (MVC) またはコントロール (Web フォーム) を介してデータにアクセスする場合、問題はありません。しかし、request.Form["inputId"] (1 つのオブジェクトを作成するときに Elmah が行う) にアクセスすると、毎回例外がスローされます。

私がしたこと:

  • Google コード プロジェクトを自分の github アカウントにエクスポートする
  • VS2013 を使用して新しいリポジトリをワークステーションに複製します
  • NETFX 4.5 ビルド構成を選択します
  • ソリューションをビルドしてデモを実行する
  • 以下を default.aspx に追加して、デモで私のケースをテストします。

ここに画像の説明を入力

  • いくつかのテストを実行する

テストに満足した後、次の方法で新しい dll をソリューションに追加しました。

  • 現在の Elmah.dll 参照の削除
  • Elmah.dll、Elmah.AspNet.dll、および AntiXssLibrary.dll をソリューションの「lib」フォルダーにコピーして貼り付けます
  • 3 つの新しい dll への参照を追加します。
  • デモの web.config のように見えるように web.config を更新します (内容は非常に似ていますが、異なります (Elmah = Elmah.AspNet))。
  • Elmah.mvc 参照を削除します (問題が発生しました)
  • FilterConfig から削除filters.Add(new HandleErrorAttribute());します (これにより、カスタム エラーが機能し続けることができます)。

Elmah ソースでは、重要な部分はrequest.UnvalidatedHttpRequestValidation.csでの使用です。

于 2015-09-27T23:41:54.680 に答える
0

プロジェクトに次のクラスを追加します。

public class ElmahErrorLogModuleFix : ErrorLogModule
{
    protected override void LogException(Exception e, HttpContext context)
    {
        if (e == null)
            throw new ArgumentNullException("e");
        ExceptionFilterEventArgs args = new ExceptionFilterEventArgs(e, (object)context);
        this.OnFiltering(args);
        if (args.Dismissed)
            return;
        ErrorLogEntry entry = (ErrorLogEntry)null;
        try
        {
            //FIX STARTS
            //Error error = new Error(e, context);
            Error error = CreateErrorSafe(e, context);
            //FIX ENDS
            ErrorLog errorLog = this.GetErrorLog(context);
            error.ApplicationName = errorLog.ApplicationName;
            string id = errorLog.Log(error);
            entry = new ErrorLogEntry(errorLog, id, error);
        }
        catch (Exception ex)
        {
            Trace.WriteLine((object)ex);
        }
        if (entry == null)
            return;
        this.OnLogged(new ErrorLoggedEventArgs(entry));
    }

    public static Error CreateErrorSafe(Exception e, HttpContext context)
    {
        try
        {
            var safeFormCollection = new NameValueCollection();
            var form = context.Request.Form;
            var additionalMessage = string.Empty;
            foreach (var key in form.AllKeys)
            {
                try
                {
                    safeFormCollection.Add(key, form[key]);
                }
                catch (Exception)
                {
                    safeFormCollection.Add(key, "_invalid input data_");
                    additionalMessage += "Form parameter with name=" + key + " has dangerous value. " + Environment.NewLine;
                }
            }

            //if no invalid values in form then do as elmah does
            if (string.IsNullOrEmpty(additionalMessage))
            {
                return new Error(e, context);
            }

            var exception = new Exception(additionalMessage, e);
            var error = new Error(exception);
            error.HostName = TryGetMachineName(context, null);

            IPrincipal user = context.User;
            if (user != null && NullString(user.Identity.Name).Length > 0)
                error.User = user.Identity.Name;
            HttpRequest request = context.Request;
            //this._serverVariables = Error.CopyCollection(request.ServerVariables);
            error.ServerVariables.Add(CopyCollection(request.ServerVariables));
            if (error.ServerVariables != null && error.ServerVariables["AUTH_PASSWORD"] != null)
                error.ServerVariables["AUTH_PASSWORD"] = "*****";
            error.QueryString.Add(CopyCollection(request.QueryString));
            error.Form.Add(CopyCollection(safeFormCollection));
            error.Cookies.Add(CopyCollection(request.Cookies));
            return error;
        }
        catch (Exception logEx)
        {
            return new Error(new Exception("Error when trying to process error catched by elmah", logEx));
        }
    }

    /// <summary>
    /// Elmah dll method in Environment.cs
    /// </summary>
    /// <param name="context"></param>
    /// <param name="unknownName"></param>
    /// <returns></returns>
    public static string TryGetMachineName(HttpContext context, string unknownName)
    {
        if (context != null)
        {
            try
            {
                return context.Server.MachineName;
            }
            catch (HttpException ex)
            {
            }
            catch (SecurityException ex)
            {
            }
        }
        try
        {
            return System.Environment.MachineName;
        }
        catch (SecurityException ex)
        {
        }
        return NullString(unknownName);
    }

    /// <summary>
    /// Elmah method in Mask.cs
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static string NullString(string s)
    {
        if (s != null)
            return s;
        else
            return string.Empty;
    }

    /// <summary>
    /// Elmah method in Error.cs
    /// </summary>
    /// <param name="collection"></param>
    /// <returns></returns>
    private static NameValueCollection CopyCollection(NameValueCollection collection)
    {
        if (collection == null || collection.Count == 0)
            //FIX HERE: cannot allow reutrn null collection as elmah does, because of exception. fix as below
            //return (NameValueCollection)null;
            return new NameValueCollection();
        //FIX ENDS
        else
            return new NameValueCollection(collection);
    }

    /// <summary>
    /// Elmah method in Error.cs
    /// </summary>
    /// <param name="cookies"></param>
    /// <returns></returns>
    private static NameValueCollection CopyCollection(HttpCookieCollection cookies)
    {
        if (cookies == null || cookies.Count == 0)
            //FIX HERE: cannot allow reutrn null collection as elmah does, because of exception. fix as below
            //return (NameValueCollection)null;
            return new NameValueCollection();
        //FIX ENDS
        NameValueCollection nameValueCollection = new NameValueCollection(cookies.Count);
        for (int index = 0; index < cookies.Count; ++index)
        {
            HttpCookie httpCookie = cookies[index];
            nameValueCollection.Add(httpCookie.Name, httpCookie.Value);
        }
        return nameValueCollection;
    }
}

public class ElmahErrorMailModuleFix : ErrorMailModule
{
    private bool _reportAsynchronously2;

    protected override void OnInit(HttpApplication application)
    {
        base.OnInit(application);
        IDictionary config = (IDictionary)this.GetConfig();
        if (config == null)
            return;
        _reportAsynchronously2 = Convert.ToBoolean(GetSetting(config, "async", bool.TrueString));
    }

    protected override void OnError(Exception e, HttpContext context)
    {
        if (e == null)
            throw new ArgumentNullException("e");
        ExceptionFilterEventArgs args = new ExceptionFilterEventArgs(e, (object)context);
        this.OnFiltering(args);
        if (args.Dismissed)
            return;
        //FIX STARTS
        //Error error = new Error(e, context);
        Error error = ElmahErrorLogModuleFix.CreateErrorSafe(e, context);
        //FIX ENDS
        if (this._reportAsynchronously2)
            this.ReportErrorAsync(error);
        else
            this.ReportError(error);
    }

    /// <summary>
    /// Elmah method in ErrorMailModule.cs
    /// </summary>
    /// <param name="config"></param>
    /// <param name="name"></param>
    /// <param name="defaultValue"></param>
    /// <returns></returns>
    private static string GetSetting(IDictionary config, string name, string defaultValue)
    {
        string str = ElmahErrorLogModuleFix.NullString((string)config[(object)name]);
        if (str.Length == 0)
        {
            if (defaultValue == null)
                throw new global::Elmah.ApplicationException(string.Format("The required configuration setting '{0}' is missing for the error mailing module.", (object)name));
            str = defaultValue;
        }
        return str;
    }
}

これらのクラスは、例外が発生しないように、クラスが作成された場所でメソッドを継承しErrorLogModuleErrorMailModuleメソッドを書き換えます。ErrorHttpRequestValidationException

次に、これらを Web.config に追加します。

<add name="ErrorLog" type="YourProject.SomeFolder.ElmahErrorLogModuleFix, YourProject" preCondition="managedHandler" />
<!--and for email module-->

元のクラスの代わりにこれらのクラスを使用します。少し汚いハックですが、機能します。

クレジットは、ここにあるメッセージ #17 のポスターにあります。

于 2016-01-06T09:08:49.563 に答える
0

HttpException通常の例外の代わりに をスローすることで、Elmah を起動させることができます。

私たちの解決策は、コントローラーのアクション コードをtry/catchブロックでcatchラップし、コードによってスローされる例外をブロックでラップし、HttpException代わりにそれをスローすることでした。このような:

[HttpPost]
[ValidateInput(false)]
public ActionResult MyAction(FormCollection riskyData)
{
    try
    {
        //... your code here ...
    }
    catch (Exception ex)
    {
        throw new HttpException(500, "Internal Server Error", ex);
    }
}
于 2013-07-11T09:48:13.597 に答える