使用 ASP.NET MVC5 中的自动化错误处理打破日志文件的习惯
借助 codeRR,您可以节省时间和精力,它能为您查找和分析错误,同时提高代码质量。
引言
当您浏览喜欢的网站时,如果网站出现故障,您会怎么做?
您会开始分析网站,尝试找出根本原因,然后将详细的错误描述通过电子邮件发送给网站支持人员吗?
我们开发者知道详细错误报告的重要性。尽管如此,只有少数人愿意花时间编写一份恰当的报告来帮助其他程序员。而普通用户报告错误的意愿则更低。如果您收到一份错误报告,几乎可以肯定,许多其他用户已经遇到过相同的错误。
我认为,依赖用户错误报告的程序员只了解其应用程序实际存在的错误的一小部分。
codeRR 是什么?
codeRR 是日志文件和用户错误报告的替代品。codeRR 可以回答这样的问题:我的应用程序抛出了多少个独特的异常?它们发生的频率有多高?有多少用户受到影响?用户在异常发生时做了什么?
有了 codeRR,您不再需要扫描日志文件来查找错误,也不需要追溯用户操作来理解为什么会抛出异常。
此外,codeRR 让您重新掌控软件缺陷,并允许您专注于编写新功能,而不是处理耗时的错误分析和理解任务。
codeRR 和 MVC
codeRR 会集成到 MVC 和 ASP.NET 管道中,以便能够捕获 MVC 中发生的所有错误和异常。
要激活 codeRR,请安装 MVC Nuget 包 (coderr.client.aspnet.mvc5
),并在 global.asax 中按以下方式配置您的应用程序:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// codeRR configuration
// appKey is found in your codeRR server
var uri = new Uri("http://yourwebserver/coderr/");
Err.Configuration.Credentials(uri,
"yourAppKey",
"yourSharedSecret");
// Tell codeRR to catch unhandled exceptions
Err.Configuration.CatchMvcExceptions();
// the usual stuff
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
稍后我们将更详细地讨论配置选项。
现在 codeRR 已经激活,假设您有以下简单的 action 和 view model:
[HttpPost]
public ActionResult Update(UpdateViewModel model)
{
var dbEntity = ToEntity(model);
_repository.Update(dbEntity);
return RedirectToAction("Updated");
}
public class UpdateViewModel
{
[Required]
public int UserId { get; set; }
[Required]
public string UserName { get; set; }
[Required]
public string FirstName { get; set; }
[StringLength(40, MinimumLength = 2)]
public string LastName { get; set; }
}
当该方法中出现问题时,例如由于数据库错误,您仍然会看到标准的“死亡黄色屏幕”。
然而,在您眨眼之间,您还会收到一封(可选的)电子邮件通知,其中包含指向新错误的链接。无论异常抛出多少次,该通知仅发送一次(每个唯一错误)。
这样做的好处是,您不再需要等待用户决定报告错误,也不需要扫描日志文件来查找它。
点击邮件中的链接,codeRR 将为您打开相关的事件。
该页面简要概述了事件被报告了多少次,有多少用户正在等待状态更新,并列出了该特定错误的报告。
您还可以访问显示报告来源的世界地图。另一个视图显示了最常与错误关联的上下文数据值,例如,如果大多数报告都与某个国家的常用 UI 文化有关。请阅读我们的入门文章(链接)以获取有关 codeRR 服务的更多信息。
您可以点击事件下的特定报告来浏览上下文数据集合。例如,下面是 RouteData 集合,我们可以看到用户采取了哪个路由。
...以及用户在 HTML 页面中提交了哪些信息。
如上例所示,codeRR 收集了来自 MVC 的大量不同信息,以便您轻松了解为什么会出错。
要了解更多关于上下文集合的信息(有 12 个集合是 ASP.NET MVC5 特有的),请访问我们的文档。
手动报告异常
尽管自动化异常处理适用于大多数用例,但有时您需要自己捕获异常。codeRR 将帮助您做到这一点。
让我们再次查看前面展示的代码示例,但现在要考虑模型状态处理。
[HttpPost]
public ActionResult Update(UpdateViewModel model)
{
if (!ModelState.IsValid)
return View(model);
try
{
var dbEntity = ToEntity(model);
_repository.Store(dbEntity);
return RedirectToAction("Updated");
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.Message);
//magic, model as context data.
this.ReportException(ex, model);
return View();
}
}
在这里,我们将 view model 包含在向 codeRR 报告错误时。
在 codeRR 网站上,view model 被表示为一个上下文集合。
所有其他上下文集合当然也包含在内。
获取自定义上下文数据
在手动报告错误时,第二个参数用于包含上下文数据。当您需要包含自定义数据时,例如 view model、已登录用户或任何有助于您理解异常原因的信息,该参数非常有用。
您可以附加任何类型的上下文数据,codeRR 支持从简单的原始类型到复杂的对象层次结构。
try
{
//[..some code..]
}
catch (Exception ex)
{
var contextData = new {
User = userModel,
Id = id
}
Err.Report(ex, contextData)
}
上面附加的对象将被呈现为:
有时您想将上下文信息分成多个集合。为此,您需要使用 ContextCollectionDTO
类。您可以使用 ToContextCollection()
扩展方法来简化代码。
// a anonymous type
var actionParameters = new { Id = id };
// create collections for our
// view model and action parameters.
var collections = new[] {
userModel.ToContextCollection("UserData"),
actionParameters.ToContextCollection("ActionParameters")
};
// And attach them to the error report.
Err.Report(exception, contextData)
当您访问 codeRR 网站上的事件时,您会看到两个不同的集合。请注意,附加的对象是纯 DTO,因为属性没有以 User.
为前缀。
验证助手
编写清晰的代码对于构建可维护的应用程序很重要(即,不要让代码质量随着时间的推移而下降)。
codeRR MVC 库中有一个属性,您可以使用它来从 action 方法中删除验证代码。
传统上,您会在所有 action 方法中重复验证逻辑,如下所示:
[HttpPost]
public ActionResult Update(UpdateViewModel model)
{
if (!ModelState.IsValid)
return View(model);
try
{
var dbEntity = ToEntity(model);
_repository.Store(dbEntity);
return RedirectToAction("Updated");
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.Message);
//magic, model as context data.
this.ReportException(ex, model);
return View();
}
}
但是,使用我们的属性,您可以保持代码的整洁:
[HttpPost, CoderrAction]
public ActionResult Update(UpdateViewModel model)
{
var dbEntity = ToEntity(model);
_repository.Store(dbEntity);
return View();
}
该属性执行以下操作:
- 验证模型,如果模型无效,则再次返回视图(带有指定的模型)。
- 如果 action 失败,则返回带有模型和验证摘要中
exception.Message
的视图。 - 异常也会连同模型一起报告给 codeRR。
清晰简单的代码可以提高质量,因为所需的假设更少,更容易理解代码的作用。上面的属性是帮助实现这一点的一种方式。
错误页面
从用户体验的角度来看,错误页面对于最大程度地减少错误可能引起的挫败感至关重要。设计精良的错误页面可以减轻痛苦,让用户知道您没有忘记他们,并将尽力解决他们的问题。
然而,与所有用户进行一对一的沟通是不现实的,这通常是错误页面保持静态的原因。
codeRR 解决了这两个挑战。我们内置了错误页面,并允许用户提交反馈(他们在错误发生时做了什么)并邀请他们关注事件。后者意味着您可以在工作进展时发送状态更新,通知所有感兴趣的用户。在最后一个状态更新中,您通常会说明错误已在哪个应用程序版本中得到修复。
以下是默认的内置错误页面(禁用了关注状态更新选项):
添加的任何注释都将在 codeRR 服务器上的相应事件下显示。
要激活错误页面,您需要在 global.asax
中添加 Err.Configuration.DisplayErrorPages();
。
内置错误页面
第一个错误页面选项是此库提供的内置错误页面。
错误页面通过使用以下属性进行配置:
//ask what the user did when the error occurred
Err.Configuration.UserInteraction.AskUserForDetails = true;
//do not send error directly, ask for permission first
Err.Configuration.UserInteraction.AskUserForPermission = true;
// ask if the user want to get status updates regarding your bug fix
Err.Configuration.UserInteraction.AskForEmailAddress = true;
要使用库中包含的页面,您只需按上述方法激活它们。
Err.Configuration.DisplayErrorPages();
默认错误页面
如果没有特定的 HTTP 错误页面,将显示此页面。
未找到页面
显示 HTTP 状态码 404。
自定义错误页面
您也可以创建自己的错误页面。只需在 Views\
下创建一个名为 Errors
的新文件夹。视图应命名为 .NET 中 HttpStatusCode
枚举中定义的 HTTP 状态码。
当找不到特定状态码视图时,将使用 Error.cshtml
视图。这允许您为身份验证失败(Unauthorized.cshtml
)或特定页面访问被拒绝(Forbidden.cshtml
)创建特定页面。
创建视图时,您只需要记住在其中使用我们的 ViewModel CoderrViewModel
。
@model codeRR.Client.AspNet.Mvc5.CoderrViewModel
<h1>Internal Server Error</h1>
<p>
We've experienced a malfunction in the core crystal cooling. The ship will explode within five seconds.
</p>
<h3>Reason</h3>
<p>
@Model.Exception.Message
</p>
为了让用户描述他们抛出异常时所做的操作,您需要在错误视图中包含一个表单。
@model codeRR.Client.AspNet.Mvc5.CoderrViewModel
<h1>Error</h1>
<img src="@Url.Content("~/Content/oops.png")" />
<div>
<h3>Reason</h3>
<p>
@Model.Exception.Message
</p>
</div>
<div>
<h3>Can you describe what you tried to do?</h3>
<form method="post" action="/coderr/submit">
@Html.HiddenFor(x => x.ReportId)
@Html.TextAreaFor(x => x.UserErrorDescription, new { rows = 5, cols = 20, @class = "form-control", placeholder = "What did you do?!" })
<br />
<button class="btn btn-primary">Send error report</button>
</form>
</div>
结果
ErrorController
当您需要比自定义视图更多的控制时,可以使用错误控制器。像在 Controllers 文件夹中的其他控制器一样创建它。
action 方法应命名为视图的名称。即 public ActionResult InternalServer(CoderrViewModel model)
。codeRR 将自动调用正确的 action 方法(对应于当前 HTTP 状态码)。
public class ErrorController : Controller
{
public ActionResult Index(CoderrViewModel model)
{
return View("Error", model);
}
public ActionResult NotFound(CoderrViewModel model)
{
return View(model);
}
public ActionResult InternalServerError(CoderrViewModel model)
{
return View(model);
}
}
其他内容类型的错误(WebApi)
codeRR 将检查客户端请求的内容类型(即,检查 Accept
http 头)。如果客户端优先使用 JSON 或 XML 而不是 HTML,则将使用以下响应而不是 HTML。
JSON
{
"error": {
"msg": "<The error message>",
"reportId": "<Unique error id>"
},
hint: "Use the report id when contacting us if you need further assistance."
}
XML
<Error ReportId="<Unique error id>" hint="Use the report id when contacting us if you need further assistance">
[Error message]
</Error>
“我为什么要使用这些错误页面?”
由于异常提供了错误,直接从用户那里获取信息很有帮助。这些错误页面在无需额外工作或编码的情况下提供了这种可能性。
错误描述将附加到报告的错误中,以便所有信息,包括来自用户的信息和收集的上下文信息,都呈现在一个地方。
结论
如果我必须说出 codeRR 中最重要的两个功能,那就是以下几点:
-
codeRR 的第一个关键功能是错误发现。由于您不必依赖日志文件扫描和用户错误报告,而是可以了解系统中的所有异常,因此您将有更大的机会创建更高质量的软件。
-
另一个关键功能是节省时间,错误管理被简化到最少,因为无需分析用户错误报告中的重复项,也无需研究日志文件来查找错误原因。
仅这两点,就足以让您在提高软件质量的道路上走得更远,并为您腾出大量时间来专注于最美妙的事情:编写新代码!
接下来做什么
那么,您怎么看?codeRR 能否帮助提高您的应用程序质量?您是否缺少任何重要功能?欢迎在评论区提供任何反馈。
想开始使用吗?