前两天刚写过一篇asp.net mvc实现 错误异常记录功能的实现。考虑到在我以前的 webform 项目中是有当出现500 错误时有友好提示功能的,也打算在 MVC 中实现此功能。在我的 LogExceptionAttribute 类的 OnException 方法中增加以下代码:
//通知MVC框架,现在这个异常已经被我处理掉,你不需要将黄页显示给用户 filterContext.ExceptionHandled = true; /跳转到错误提醒页面 filterContext.Result = new ViewResult { ViewName = "error" }; 跳转的页面是error.cshtml , error.cshtml 在网站根目录的 views/shared 下,代码如下:
@model System.Web.Mvc.HandleErrorInfo @{ Layout = null; ViewBag.Title = "错误"; } <html> <body> <h3 style="color:#6699cc;border-bottom:1px solid #8cb2d9;padding-left:20px;">【 错误提示 】</h3> <div style="padding-left:45px;line-height:35px;color:#666"> 很抱歉,您要访问的页面无法正确显示,可能是因为如下原因:<br /> 1 . 系统过于繁忙,请点击浏览器的“刷新”按钮,或稍后再试。<br /> 2 . 您输入的网址有误,请重新检查您输入的网址。 <br /> 3 . 此页面已经删除,请访问其他页面。 <br /> 4 . <a href="/">返回首页</a> <br /> </div> </body> </html> 这里的Layout 要设置为等于 null ,否则 error.cshtml 页面会继承默认的视图模板。这就大致实现了,当有500错误时,自动跳转到 error 页面显示友好提示信息。但还有瑕疵。第一个,在本地测试时,自然是不需要 友好提示的。因此需要增加如果是本地,则不显示友好提示。
if (!Utility.IsLocal()) //非本地,并且需要自定义错误时执行 { //通知MVC框架,现在这个异常已经被我处理掉,你不需要将黄页显示给用户 filterContext.ExceptionHandled = true; //跳转到错误提醒页面 filterContext.Result = new ViewResult { ViewName = "error" }; } IsLocal 方法的代码:
/// <summary> /// 是否本地服务器 /// </summary> /// <returns></returns> public static bool IsLocal() { string address = RequestHelper.GetServerIp(); //本地不作处理 if (address.Contains("192.168.")) { return true; } return false; } 第二个瑕疵,如果是json 请求,而出现的错误,这个跳转就无能为力了。因此需要为json 异常单独处理。创建json 异常过滤器,代码如下:
/// <summary> /// json异常处理,只能使用在action上。调用方法 /// 在json的action上直接使用[JsonException] /// </summary> [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class JsonExceptionAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled) { //返回异常JSON //filterContext.Result = new JsonResult //{ // Data = new { Success = false, Message = filterContext.Exception.Message } //}; //{"Message":"尝试除以零。"} //filterContext.Result = new JsonResult { Data=new { filterContext.Exception.Message } }; //{"str":"尝试除以零。"} //var str = filterContext.Exception.Message; //filterContext.Result = new JsonResult { Data = new { str } }; var json = new JsonResult(); json.Data = filterContext.Exception.Message; filterContext.Result = json; } } } 这里有个坑,由于我的json 异常信息习惯使用的是 return Json("删除成功");这样的方式返回给客户端 ,客户端通常使用 alert或者其他弹出插件直接获取返回的信息。因此在写代码时首先使用的filterContext.Result = new JsonResult { Data=new { filterContext.Exception.Message } }; 这代码在测试中发现没有弹出提示。用 fiddler 查看json 返回的值却发现是{"Message":"尝试除以零。"} 这种形式,这并不是我想要的。又尝试如下代码:
var str = filterContext.Exception.Message; filterContext.Result = new JsonResult { Data = new { str } }; 通过 fiddler 查看,返回值变成了:{"str":"尝试除以零。"} ,好不郁闷。最终多次尝试后使用
var json = new JsonResult(); json.Data = filterContext.Exception.Message; filterContext.Result = json;可以得到我想要的结果。调用就是在返回Jsonresult 结果的 action上增加 [JsonException] ,代码如下:
/// <summary> /// 修改密码 /// </summary> /// <param name="oldPassword"></param> /// <param name="newPassword"></param> /// <returns></returns> [HttpPost] [JsonException] public async Task<JsonResult> ChangePassword(string oldPassword, string newPassword) 实现这个功能后,还需要对 LogExceptionAttribute 类进行修改,当 判断为json 请求时不做处理。修改好的代码如下:
/// <summary> /// 错误日志记录 /// </summary> [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] public class LogExceptionAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled) { #region 记录错误日志 string controllerName = (string)filterContext.RouteData.Values["controller"]; string actionName = (string)filterContext.RouteData.Values["action"]; string msgTemplate = string.Format("在执行 controller[{0}] 的 action[{1}] 时产生异常", controllerName, actionName); EventLog.LogItem item = new EventLog.LogItem(); item.Title = msgTemplate; LogManage.WriteException(filterContext.Exception, item); #endregion if (!(filterContext.Result is JsonResult)) //当非JsonResult并且需要自定义错误页面时 { if (!Utility.IsLocal()) //非本地,并且需要自定义错误时执行 { //通知MVC框架,现在这个异常已经被我处理掉,你不需要将黄页显示给用户 filterContext.ExceptionHandled = true; //跳转到错误提醒页面 filterContext.Result = new ViewResult { ViewName = "error" }; } } } if (filterContext.Result is JsonResult) { //当结果为json时,设置异常已处理,此时要在action上调用JsonException属性 filterContext.ExceptionHandled = true; } else { //否则调用原始设置 base.OnException(filterContext); } } } 这样就完整的实现了 MVC 中无论是页面,还是 json 请求当出现错误时都有友好提示功能了