プロジェクトに次のクラスを追加します。
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;
}
}
これらのクラスは、例外が発生しないように、クラスが作成された場所でメソッドを継承しErrorLogModule
、ErrorMailModule
メソッドを書き換えます。Error
HttpRequestValidationException
次に、これらを Web.config に追加します。
<add name="ErrorLog" type="YourProject.SomeFolder.ElmahErrorLogModuleFix, YourProject" preCondition="managedHandler" />
<!--and for email module-->
元のクラスの代わりにこれらのクラスを使用します。少し汚いハックですが、機能します。
クレジットは、ここにあるメッセージ #17 のポスターにあります。