异常信息处理是任何网站必不可少的一个环节,怎么有效显示,记录,传递异常信息又成为重中之重的问题。本篇将基于上篇介绍的html2cancas截图功能,实现mvc自定义全局异常处理。先看一下最终实现效果:http://yanweidie.myscloud.cn/Home/Index

阅读目录

我理解中好的异常处理

    好的异常信息处理应该具有以下几个优点

  • 显示效果佳,而不是原生黄页
  • 能够从异常中直接分析出异常源
  • 能够记录传递异常信息给开发人员

1.第一点显示效果方面可以自定义页面,常见的包括404和500状态码页面。在mvc中404页面可以通过以下两种方式进行自定义

<system.web>
<!--添加customErrors节点 定义404跳转页面-->
<customErrors mode="On">
<error statusCode="404" redirect="/Error/Path404" />
</customErrors>
</system.web>
//Global文件的EndRequest监听Response状态码
protected void Application_EndRequest()
{
  var statusCode = Context.Response.StatusCode;
var routingData = Context.Request.RequestContext.RouteData;
if (statusCode == || statusCode == )
{
  Response.Clear();
Response.RedirectToRoute("Default", new { controller = "Error", action = "Path404" });
}
}  
      2.第二点 异常信息应该详细,能够记录下请求参数,请求地址,浏览器版本服务器和当前用户等相关信息,这就需要对异常信息记录改造加工
      3.第三点 常见的异常信息都是记录在日志文件里面,日志文件过大时也不太好分析。发生异常时要是能马上将异常信息通过邮件或者图片等方式发给开发者,可以加快分析速度。

自定义异常处理

  

  这里采用mvc的过滤器进行异常处理,分别为接口500错误和页面500错误进行处理,接口部分异常需要记录请求参数,方便分析异常。

首先定义了异常信息实体,异常实体包含了 请求地址类型(页面,接口),服务器相关信息(位数,CPU,操作系统,iis版本),客户端信息(UserAgent,HttpMethod,IP)

   异常实体代码如下

    /// <summary>
/// 系统错误信息
/// </summary>
public class ErrorMessage
{
public ErrorMessage()
{ }
public ErrorMessage(Exception ex,string type)
{
MsgType = ex.GetType().Name;
Message = ex.InnerException != null ? ex.InnerException.Message : ex.Message;
StackTrace = ex.StackTrace;
Source = ex.Source;
Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
Assembly = ex.TargetSite.Module.Assembly.FullName;
Method = ex.TargetSite.Name;
Type = type; DotNetVersion = Environment.Version.Major + "." + Environment.Version.Minor + "." + Environment.Version.Build + "." + Environment.Version.Revision;
DotNetBit = (Environment.Is64BitProcess ? "" : "") + "位";
OSVersion = Environment.OSVersion.ToString();
CPUCount = Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS");
CPUType = Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER");
OSBit = (Environment.Is64BitOperatingSystem ? "" : "") + "位"; var request = HttpContext.Current.Request;
IP = GetIpAddr(request) + ":" + request.Url.Port;
IISVersion = request.ServerVariables["SERVER_SOFTWARE"];
UserAgent = request.UserAgent;
Path = request.Path;
HttpMethod = request.HttpMethod;
}
/// <summary>
/// 消息类型
/// </summary>
public string MsgType { get; set; } /// <summary>
/// 消息内容
/// </summary>
public string Message { get; set; } /// <summary>
/// 请求路径
/// </summary>
public string Path { get; set; } /// <summary>
/// 程序集名称
/// </summary>
public string Assembly { get; set; } /// <summary>
/// 异常参数
/// </summary>
public string ActionArguments { get; set; } /// <summary>
/// 请求类型
/// </summary>
public string HttpMethod { get; set; } /// <summary>
/// 异常堆栈
/// </summary>
public string StackTrace { get; set; } /// <summary>
/// 异常源
/// </summary>
public string Source { get; set; } /// <summary>
/// 服务器IP 端口
/// </summary>
public string IP { get; set; } /// <summary>
/// 客户端浏览器标识
/// </summary>
public string UserAgent { get; set; } /// <summary>
/// .NET解释引擎版本
/// </summary>
public string DotNetVersion { get; set; } /// <summary>
/// 应用程序池位数
/// </summary>
public string DotNetBit { get; set; } /// <summary>
/// 操作系统类型
/// </summary>
public string OSVersion { get; set; } /// <summary>
/// 操作系统位数
/// </summary>
public string OSBit { get; set; } /// <summary>
/// CPU个数
/// </summary>
public string CPUCount { get; set; } /// <summary>
/// CPU类型
/// </summary>
public string CPUType { get; set; } /// <summary>
/// IIS版本
/// </summary>
public string IISVersion { get; set; } /// <summary>
/// 请求地址类型
/// </summary>
public string Type { get; set; } /// <summary>
/// 是否显示异常界面
/// </summary>
public bool ShowException { get; set; } /// <summary>
/// 异常发生时间
/// </summary>
public string Time { get; set; } /// <summary>
/// 异常发生方法
/// </summary>
public string Method { get; set; } //这段代码用户请求真实IP
private static string GetIpAddr(HttpRequest request)
{
//HTTP_X_FORWARDED_FOR
string ipAddress = request.ServerVariables["x-forwarded-for"];
if (!IsEffectiveIP(ipAddress))
{
ipAddress = request.ServerVariables["Proxy-Client-IP"];
}
if (!IsEffectiveIP(ipAddress))
{
ipAddress = request.ServerVariables["WL-Proxy-Client-IP"];
}
if (!IsEffectiveIP(ipAddress))
{
ipAddress = request.ServerVariables["Remote_Addr"];
if (ipAddress.Equals("127.0.0.1") || ipAddress.Equals("::1"))
{
// 根据网卡取本机配置的IP
IPAddress[] AddressList = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
foreach (IPAddress _IPAddress in AddressList)
{
if (_IPAddress.AddressFamily.ToString() == "InterNetwork")
{
ipAddress = _IPAddress.ToString();
break;
}
}
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.Length > )
{
if (ipAddress.IndexOf(",") > )
{
ipAddress = ipAddress.Substring(, ipAddress.IndexOf(","));
}
}
return ipAddress;
} /// <summary>
/// 是否有效IP地址
/// </summary>
/// <param name="ipAddress">IP地址</param>
/// <returns>bool</returns>
private static bool IsEffectiveIP(string ipAddress)
{
return !(string.IsNullOrEmpty(ipAddress) || "unknown".Equals(ipAddress, StringComparison.OrdinalIgnoreCase));
}
}

上面代码中用到了获取客户端请求IP的方法,用于获取请求来源的真实IP。

基础异常信息定义完后,剩下的是异常记录和页面跳转了,mvc中的异常过滤器实现如下。

 /// <summary>
/// 全局页面控制器异常记录
/// </summary>
public class CustomErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
base.OnException(filterContext); ErrorMessage msg = new ErrorMessage(filterContext.Exception, "页面");
msg.ShowException = MvcException.IsExceptionEnabled(); //错误记录
LogHelper.WriteLog(JsonConvert.SerializeObject(msg, Formatting.Indented), null); //设置为true阻止golbal里面的错误执行
filterContext.ExceptionHandled = true;
filterContext.Result = new ViewResult() { ViewName = "/Views/Error/ISE.cshtml", ViewData = new ViewDataDictionary<ErrorMessage>(msg) }; }
} /// <summary>
/// 全局API异常记录
/// </summary>
public class ApiHandleErrorAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext filterContext)
{
base.OnException(filterContext); //异常信息
ErrorMessage msg = new ErrorMessage(filterContext.Exception, "接口");
//接口调用参数
msg.ActionArguments = JsonConvert.SerializeObject(filterContext.ActionContext.ActionArguments, Formatting.Indented);
msg.ShowException = MvcException.IsExceptionEnabled(); //错误记录
string exMsg = JsonConvert.SerializeObject(msg, Formatting.Indented);
LogHelper.WriteLog(exMsg, null); filterContext.Response = new HttpResponseMessage() { StatusCode = HttpStatusCode.InternalServerError, Content = new StringContent(exMsg) };
}
} /// <summary>
/// 异常信息显示
/// </summary>
public class MvcException
{
/// <summary>
/// 是否已经获取的允许显示异常
/// </summary>
private static bool HasGetExceptionEnabled = false; private static bool isExceptionEnabled; /// <summary>
/// 是否显示异常信息
/// </summary>
/// <returns>是否显示异常信息</returns>
public static bool IsExceptionEnabled()
{
if (!HasGetExceptionEnabled)
{
isExceptionEnabled = GetExceptionEnabled();
HasGetExceptionEnabled = true;
}
return isExceptionEnabled;
} /// <summary>
/// 根据Web.config AppSettings节点下的ExceptionEnabled值来决定是否显示异常信息
/// </summary>
/// <returns></returns>
private static bool GetExceptionEnabled()
{
bool result;
if(!Boolean.TryParse(ConfigurationManager.AppSettings["ExceptionEnabled"],out result))
{
return false;
}
return result;
}
}

值得注意的是上面的MvcException类的GetExceptionEnabled方法,该方法从web.config appsetting中读取节点"ExceptionEnabled"来控制异常信息是否初始化显示。异常信息除了显示在页面,还使用了log4net组件记录在错误日志中,方便留痕。

过滤器定义完成后,需要在filterconfig添加引用

    public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomErrorAttribute());
filters.Add(new HandleErrorAttribute());
}
}

问题拓展

  后台异常处理代码完成以后,前台还需进行相应的处理。这里主要针对api接口,因为请求页面后台可以直接转向500错误页面,而api接口一般是通过ajax或者客户端httpclient请求的,如果错误了跳转到500页面,这样对客户端来说就不友好了。基于这点所以api请求异常返回了异常的详细json对象,让客户端自己进行异常处理。我这里给出ajax处理异常的方式。

在jquery中全局ajax请求可以设置相应默认参数,比如下面代码设置了全局ajax请求为异步请求,不缓存

//ajax请求全局设置
$.ajaxSetup({
//异步请求
async: true,
//缓存设置
cache: false
});

ajax请求完成会触发Complete事件,在jquery中全局Complete事件可以通过下面代码监听

$(document).ajaxComplete(function (evt, request, settings) {
var text = request.responseText;
if (text) {
try {
//Unauthorized 登录超时或者无权限
if (request.status == "401") {
var json = $.parseJSON(text);
if (json.Message == "logout") {
//登录超时,弹出系统登录框
} else {
layer.alert(json.ExceptionMessage ? json.ExceptionMessage : "系统异常,请联系系统管理员", {
title: "错误提醒",
icon: 2
});
}
} else if (request.status == "500") {
var json = $.parseJSON(text);
$.ajax({
type: "post",
url: "/Error/Path500",
data: { "": json },
data: json,
dataType: "html",
success: function (data) {
//页面层
layer.open({
title: '异常信息',
type: 1,
shade: 0.8,
shift: -1,
area: ['100%', '100%'],
content: data,
});
}
}); }
} catch (e) {
console.log(e);
}
}
});
红色部分代码就是我用来处理500错误的代码,重新发请求到异常显示界面渲染成html后显示。其实这么做无疑增加了一次请求,最好的实现方式,直接通过异常信息json,通过js绘制出html。至此完成了mvc全局的页面,接口异常信息处理。通过结合上面的前端截图插件,快速截图留证,方便后续程序员分析异常信息。

总结

  通过一点小小的改造,我们完成了一个既美观又方便拓展的错误处理方式。看到上面萌萌的图片你是否心动了,想马上下载代码体验一把呢。下面就给出本文所有的源代码:

git代码地址:https://github.com/CrazyJson/CustomGlobalError

预告一下,下一篇将会对之前的TaskManager管理平台进行升级,主要实现管理界面方便查看当前运行的所有任务和管理任务。讲解管理平台运用到的技术,敬请期待!

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我

如果,想给予我更多的鼓励,求打

因为,我的写作热情也离不开您的肯定支持。

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是焰尾迭 。

mvc自定义全局异常处理的更多相关文章

  1. ASP.NET Core 中间件自定义全局异常处理

    目录 背景 ASP.NET Core过滤器(Filter) ASP.NET Core 中间件(Middleware) 自定义全局异常处理 .Net Core中使用ExceptionFilter .Ne ...

  2. Asp.net mvc 自定义全局的错误事件HandleErrorAttribute无效

    Asp.net mvc 自定义全局的错误事件HandleErrorAttribute,结果无效, 原因: 1.没有在RegisterGlobalFilters 里面添加或者你要的位置添加. 2.你把这 ...

  3. Laravel实践-自定义全局异常处理

    在做API时,需要对一些异常进行全局处理 百牛信息技术bainiu.ltd整理发布于博客园比如添加用户执行失败时,需要返回错误信息 // 添加用户 $result = User::add($user) ...

  4. Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志

    在使用SimpleMappingExceptionResolver实现统一异常处理后(参考Spring MVC的异常统一处理方法), 发现出现异常时,log4j无法在控制台输出错误日志.因此需要自定义 ...

  5. asp.net mvc 自定义全局过滤器 验证用户是否登录

    一般具有用户模块的系统都需要对用户是否登录进行验证,如果用户登录了就可以继续操作,否则退回用户的登录页面 对于这样的需求我们可以通过自定义一个独立的方法来完成验证的操作,但是这样代码的重复率就大大提高 ...

  6. ASP.NET MVC中全局异常处理

    以前不知道从哪里找到的处理全局异常的,觉得蛮好用就记下来了. 1, 建立MyExecptionAttribute.cs类,写入如下代码: using System; using System.Coll ...

  7. springMvc全局异常处理

    本文中只测试了:实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器 对已有代码没有入侵性等优点,同时,在异常处理时能获取导致出现异常的对象,有利于提 ...

  8. 第6章 AOP与全局异常处理6.1-6.4 慕课网微信小程序开发学习笔记

    第6章 AOP与全局异常处理 https://coding.imooc.com/learn/list/97.html 目录: 第6章 AOP与全局异常处理6-1 正确理解异常处理流程 13:236-2 ...

  9. SpringBoot优雅的全局异常处理

    前言 本篇文章主要介绍的是SpringBoot项目进行全局异常的处理. SpringBoot全局异常准备 说明:如果想直接获取工程那么可以直接跳到底部,通过链接下载工程代码. 开发准备 环境要求 JD ...

随机推荐

  1. javascript俄罗斯方块游戏

    在线试玩:http://keleyi.com/game/5/ 操作指南:键盘方向键←→控制左右移动,↑键变形,↓键快速下落. 别看这段js代码只有短短的100多行,效果却非常不错,用键盘的方向键操作, ...

  2. sharepoint2013用场管理员进行文档库的爬网提示"没有权限,拒绝"的解决方法

    爬网提示被拒绝,场管理员明明可以打开那个站点的,我初步怀疑是:环回请求(LoopbackRequest)导致的 解决方法就是修改环回问题.修改注册表 具体操作方法: http://www.c-shar ...

  3. SharePoint Permission Extension

    SharePoint Permission Extension 项目很久没维护了,也没有迁移到sp2013上(貌似只要把2013的Form的RenderMode设置为Server后也是可以用的). 在 ...

  4. php 与 java 生成时间戳的区别

    最近服务器有java却换到php环境,生成的时间戳转换成时间格式的出现异常,查询资料得知: PHP 的 time() 函数返回的结果是 Unix 时间戳,值的单位是秒:如:1463564861 Jav ...

  5. Django model.py表单的默认值 默认允许为空

    Field.null 默认是null=False,这时不能传入NULL型数据,但是可以是空字符. 如果BooleanField中想要null数据类型,可以选择NullBooleanField   Fi ...

  6. TFS 10周年生日快乐 – TFS与布莱恩大叔的故事

    今天看了一下Brian Harry大叔的博客,才发现2016年3月17日,是Team Foundation Server的10岁生日. Today marks the 10th anniversary ...

  7. 【从零开始学习Hadoop】--2.HDFS分布式文件系统

    1. 文件系统从头说2. Hadoop的文件系统3. 如何将文件复制到HDFS3.1 目录和文件结构3.2 FileCopy.java文件的源代码3.3 编译3.4打包3.5 运行3.6 检查结果 1 ...

  8. ORACLE清理、截断监听日志文件(listener.log)

    在ORACLE数据库中,如果不对监听日志文件(listener.log)进行截断,那么监听日志文件(listener.log)会变得越来越大,想必不少人听说过关于"LISTENER.LOG日 ...

  9. Linux LVM学习总结——创建卷组VG

    在Linux平台如何创建一个卷组(VG)呢?下面简单介绍一下卷组(VG)的创建步骤.本文实验平台为Red Hat Enterprise Linux Server release 6.6 (Santia ...

  10. Attempt to fetch logical page (...) in database 2 failed. It belongs to allocation unit xxxx not to xxx

    今天一个同事说在一个生产库执行某个存储过程,遇到了错误: Fatal error 605 occurred at jul 29 2014 我试着执行该存储过程,结果出现下面错误,每次执行该存储过程,得 ...