mvc自定义全局异常处理

时间:2022-09-03 10:05:52

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

mvc自定义全局异常处理mvc自定义全局异常处理

阅读目录

我理解中好的异常处理

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

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

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自定义全局异常处理

  这里采用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&period;NET Core 中间件自定义全局异常处理

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

  2. Asp&period;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&period;net mvc 自定义全局过滤器 验证用户是否登录

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

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

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

  7. springMvc全局异常处理

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

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

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

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

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

随机推荐

  1. JAVA 笔试笔记

    1.java优缺点 优点 :纯面对对象,跨平台,提供很多内置的类库,支持web开发,有较好的健壮性和安全性缺点 : 速度慢,跨平台不能像其他的语言一样接近操作系统,复杂 2.java与c/c++ 都是 ...

  2. 003&period;android资源文件剖析&lpar;Resources&rpar;

    android的资源文件使用的重要性,不言而喻.让我们从潜到深逐渐来了解吧. 一:android的基本资源: 1.字符串资源 android的资源文件保存在:res\values\ 2.布局资源 an ...

  3. 关于UIFont和计算字符串的高度和宽度

    转自:http://i.cnblogs.com/EditPosts.aspx?opt=1 1.创建方法:+ fontWithName:size:- fontWithSize:2.创建系统字体:+ sy ...

  4. python数组查找算法---bisect二分查找插入

    1 实例 这个模块只有几个函数, 一旦决定使用二分搜索时,立马要想到使用这个模块 [python] view plaincopyprint? import bisect L = [1,3,3,6,8, ...

  5. R语言并行计算中的内存控制

    R语言使用向量化计算,因此非常容易在集群上进行并行计算.parallel 包提供了非常方便的函数用来进行并行计算,但有一个问题是并行时对于内存中的对象会拷贝多份,因此会比较占内存,这里提供一个比较简易 ...

  6. 2017年java面试题【集合篇】

    Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个特征. 这里有10个经典的Java面试题,也为大 ...

  7. 本博客停止更新改用wordperss

    http://www.azurew.com/ 还是能有自己的博客比较爽 哈哈哈

  8. &lbrack;LintCode&rsqb; Permutations

    http://www.lintcode.com/en/problem/permutations/# Given a list of numbers, return all possible permu ...

  9. 表单如何与Servlet控制器关联

    <form action="loing.do" method="post">                <div>          ...

  10. 2cmd 窗口 javac 错误:编码GBK的不可映射字符

    错误截图: 解决办法:第一步 第二步: