我在MVC4(RC)应用程序中遇到了一个奇怪的问题。 (在.NET 4.0上运行)
我刚刚设置了Elmah来记录异常/错误。
我基本上安装了 Elmah.MVC 和 elmah.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而无法记录异常,这似乎是一个非常不幸的情况。
肯定有办法解决这个问题吗?
答案 0 :(得分:3)
默认情况下,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));
}
}
答案 1 :(得分:2)
我现在有一个工作,有人在http://code.google.com/p/elmah/issues/detail?id=217
上建议您可以强制ASP使用较旧的请求验证逻辑,方法是将其添加到<system.web>
的{{1}}部分:
Web.config
这将影响全球的应用程序,而这种情况并非如此。
如果有人有更好的事情,请告诉我。
答案 2 :(得分: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;
}
答案 3 :(得分:1)
Elmah中有一个错误已经修复了一段时间,但我不相信Google代码网站上的任何版本都已经足够包含它了。
正如我在评论中提到的,问题是如果你通过模型(MVC)或控件(webforms)访问数据,没问题;但是如果你访问request.Form [&#34; inputId&#34;](Elmah在构建一个对象时会这样做),它每次都会抛出一个异常。
我做了什么:
在我对测试感到满意后,我通过以下方式将新dll添加到我的解决方案中:
filters.Add(new HandleErrorAttribute());
(这允许自定义错误继续有效)在Elmah源代码中,关键部分是在HttpRequestValidation.cs中使用request.Unvalidated
答案 4 :(得分: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);
}
}
答案 5 :(得分: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;
}
}
这些类继承自ErrorLogModule
和ErrorMailModule
并重写创建Error
类的方法,因此HttpRequestValidationException
异常不会引发。
然后将这些添加到您的Web.config:
<add name="ErrorLog" type="YourProject.SomeFolder.ElmahErrorLogModuleFix, YourProject" preCondition="managedHandler" />
<!--and for email module-->
使用这些类而不是原始类。有点脏,但它确实有效。
显示here消息#17的海报。