ASP.NET MVC 异常Exception拦截
一、前言
由于客户端的环境不一致,有可能会造成我们预计不到的异常错误,所以在项目中,友好的异常信息提示,是非常重要的。在asp.net mvc中实现异常属性拦截也非常简单,只需要继承另一个类(System.Web.Mvc.FilterAttribute)和一个接口(System.Web.Mvc.IExceptionFilter),实现接口里面OnException方法,或者直接继承Mvc 提供的类System.Web.Mvc.HandleErrorAttribute。
二、实现关键逻辑
继承System.Web.Mvc.HandleErrorAttribute,重写了OnException方法,主要实现逻辑代码如下:
- public class HandlerErrorAttribute : HandleErrorAttribute
- {
- /// <summary>
- /// 控制器方法中出现异常,会调用该方法捕获异常
- /// </summary>
- /// <param name="context">提供使用</param>
- public override void OnException(ExceptionContext context)
- {
- WriteLog(context);
- base.OnException(context);
- context.ExceptionHandled = true;
- if (context.Exception is UserFriendlyException)
- {
- context.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
- context.Result = new ContentResult { Content = new AjaxResult { type = ResultType.error, message = context.Exception.Message }.ToJson() };
- }
- else if (context.Exception is NoAuthorizeException)
- {
- context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
- if (!context.HttpContext.Request.IsAjaxRequest())
- {
- context.HttpContext.Response.RedirectToRoute("Default", new { controller = "Error", action = "Error401", errorUrl = context.HttpContext.Request.RawUrl });
- }
- else
- {
- context.Result = new ContentResult { Content = context.HttpContext.Request.RawUrl };
- }
- }
- else
- {
- context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
- ExceptionMessage error = new ExceptionMessage(context.Exception);
- var s = error.ToJson();
- if (!context.HttpContext.Request.IsAjaxRequest())
- {
- context.HttpContext.Response.RedirectToRoute("Default", new { controller = "Error", action = "Error500", data = WebHelper.UrlEncode(s) });
- }
- else
- {
- context.Result = new ContentResult { Content = WebHelper.UrlEncode(s) };
- }
- }
- }
- /// <summary>
- /// 写入日志(log4net)
- /// </summary>
- /// <param name="context">提供使用</param>
- private void WriteLog(ExceptionContext context)
- {
- if (context == null)
- return;
- if (context.Exception is NoAuthorizeException || context.Exception is UserFriendlyException)
- {
- //友好错误提示,未授权错误提示,记录警告日志
- LogHelper.Warn(context.Exception.Message);
- }
- else
- {
- //异常错误,
- LogHelper.Error(context.Exception);
- ////TODO :写入错误日志到数据库
- }
- }
- }
MVC 过滤器全局注册异常拦截:
- public class FilterConfig
- {
- public static void RegisterGlobalFilters(GlobalFilterCollection filters)
- {
- filters.Add(new HandlerErrorAttribute());
- }
- }
我们看到,context.Exception 分为3种:UserFriendlyException,NoAuthorizeException 或 Exception;UserFriendlyException 是指友好异常,前端友好提示错误信息。NoAuthorizeException 为401未授权异常,当页面未被授权访问时,返回该异常,并携带有未授权的路径地址。其他异常统一返回500错误,并携带异常信息。
三、异常处理
1.401 未授权错误
异常定义代码:
- /// <summary>
- /// 没有被授权的异常
- /// </summary>
- public class NoAuthorizeException : Exception
- {
- public NoAuthorizeException(string message)
- : base(message)
- {
- }
- }
抛出异常代码:
- throw new NoAuthorizeException("未授权");
前端UI效果:

2.404 未找到页面错误
MVC的404异常处理,有几种方式,我们采用了在Global.asax全局请求函数中处理, 请查看以下代码
- protected void Application_EndRequest()
- {
- if (Context.Response.StatusCode == 404)
- {
- bool isAjax = new HttpRequestWrapper(Context.Request).IsAjaxRequest();
- if (isAjax)
- {
- Response.Clear();
- Response.Write(Context.Request.RawUrl);
- }
- else
- {
- Response.RedirectToRoute("Default", new { controller = "Error", action = "Error404", errorUrl = Context.Request.RawUrl });
- }
- }
- }
前端UI效果:

3.500服务器内部错误
500异常错误抛出的异常信息对象定义:
- /// <summary>
- /// 异常错误信息
- /// </summary>
- [Serializable]
- public class ExceptionMessage
- {
- public ExceptionMessage()
- {
- }
- /// <summary>
- /// 构造函数
- /// 默认显示异常页面
- /// </summary>
- /// <param name="ex">异常对象</param>
- public ExceptionMessage(Exception ex)
- :this(ex, true)
- {
- }
- /// <summary>
- /// 构造函数
- /// </summary>
- /// <param name="ex">异常对象</param>
- /// <param name="isShowException">是否显示异常页面</param>
- public ExceptionMessage(Exception ex, bool isShowException)
- {
- MsgType = ex.GetType().Name;
- Message = ex.InnerException != null ? ex.InnerException.Message : ex.Message;
- StackTrace = ex.StackTrace.Length > 300 ? ex.StackTrace.Substring(0, 300) : 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;
- ShowException = isShowException;
- var request = HttpContext.Current.Request;
- IP = Net.Ip;
- 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>
- /// 是否显示异常界面
- /// </summary>
- public bool ShowException { get; set; }
- /// <summary>
- /// 异常发生时间
- /// </summary>
- public string Time { get; set; }
- /// <summary>
- /// 异常发生方法
- /// </summary>
- public string Method { get; set; }
- }
抛出异常代码:
- throw new Exception("出错了");
前端UI效果:

4. UserFriendlyException 友好异常
异常定义代码:
- /// <summary>
- /// 用户友好异常
- /// </summary>
- public class UserFriendlyException : Exception
- {
- public UserFriendlyException(string message)
- : base(message)
- {
- }
- }
在异常拦截关键代码中,我们发现友好异常(UserFriendlyException)其实是返回了一个结果对象AjaxResult,

AjaxResult对象的定义:
- /// <summary>
- /// 表示Ajax操作结果
- /// </summary>
- public class AjaxResult
- {
- /// <summary>
- /// 获取 Ajax操作结果类型
- /// </summary>
- public ResultType type { get; set; }
- /// <summary>
- /// 获取 Ajax操作结果编码
- /// </summary>
- public int errorcode { get; set; }
- /// <summary>
- /// 获取 消息内容
- /// </summary>
- public string message { get; set; }
- /// <summary>
- /// 获取 返回数据
- /// </summary>
- public object resultdata { get; set; }
- }
- /// <summary>
- /// 表示 ajax 操作结果类型的枚举
- /// </summary>
- public enum ResultType
- {
- /// <summary>
- /// 消息结果类型
- /// </summary>
- info = 0,
- /// <summary>
- /// 成功结果类型
- /// </summary>
- success = 1,
- /// <summary>
- /// 警告结果类型
- /// </summary>
- warning = 2,
- /// <summary>
- /// 异常结果类型
- /// </summary>
- error = 3
- }
四、Ajax请求异常时处理
在异常拦截的关键代码中,我们有看到,如果是ajax请求时,是执行不同的逻辑,这是因为ajax的请求,不能直接通过MVC的路由跳转,在请求时必须返回结果内容


然后在前端ajax的方法中,统一处理返回的错误,以下是我们项目中用到的ajax封装,对异常错误,进行了统一处理。
- (function ($) {
- "use strict";
- $.httpCode = {
- success: "",
- fail: "",
- };
- // http 通信异常的时候调用此方法
- $.httpErrorLog = function (msg) {
- console.log('=====>' + new Date().getTime() + '<=====');
- console.log(msg);
- };
- // ajax请求错误处理
- $.httpError = function (xhr, textStatus, errorThrown) {
- if (xhr.status == 401) {
- location.href = "/Error/Error401?errorUrl=" + xhr.responseText;
- }
- if (xhr.status == 404) {
- location.href = "/Error/Error404?errorUrl=" + xhr.responseText;
- }
- if (xhr.status == 500) {
- location.href = "/Error/Error500?data=" + xhr.responseText;
- }
- };
- /* get请求方法(异步):
- * url地址, param参数, callback回调函数 beforeSend 请求之前回调函数, complete 请求完成之后回调函数
- * 考虑到get请求一般将参数与url拼接一起传递,所以将param参数放置最后
- * 返回AjaxResult结果对象
- */
- $.httpAsyncGet = function (url, callback, beforeSend, complete, param) {
- $.ajax({
- url: url,
- data: param,
- type: "GET",
- dataType: "json",
- async: true,
- cache: false,
- success: function (data) {
- if ($.isFunction(callback)) callback(data);
- },
- error: function (XMLHttpRequest, textStatus, errorThrown) {
- $.httpError(XMLHttpRequest, textStatus, errorThrown);
- },
- beforeSend: function () {
- if (!!beforeSend) beforeSend();
- },
- complete: function () {
- if (!!complete) complete();
- }
- });
- };
- /* get请求方法(同步):
- * url地址,param参数
- * 返回实体数据对象
- */
- $.httpGet = function (url, param) {
- var res = {};
- $.ajax({
- url: url,
- data: param,
- type: "GET",
- dataType: "json",
- async: false,
- cache: false,
- success: function (data) {
- res = data;
- },
- error: function (XMLHttpRequest, textStatus, errorThrown) {
- $.httpError(XMLHttpRequest, textStatus, errorThrown);
- },
- });
- return res;
- };
- /* post请求方法(异步):
- * url地址, param参数, callback回调函数 beforeSend 请求之前回调函数, complete 请求完成之后回调函数
- * 返回AjaxResult结果对象
- */
- $.httpAsyncPost = function (url, param, callback, beforeSend, complete) {
- $.ajax({
- url: url,
- data: param,
- type: "POST",
- dataType: "json",
- async: true,
- cache: false,
- success: function (data) {
- if ($.isFunction(callback)) callback(data);
- },
- error: function (XMLHttpRequest, textStatus, errorThrown) {
- $.httpError(XMLHttpRequest, textStatus, errorThrown);
- },
- beforeSend: function () {
- if (!!beforeSend) beforeSend();
- },
- complete: function () {
- if (!!complete) complete();
- }
- });
- };
- /* post请求方法(同步):
- * url地址,param参数, callback回调函数
- * 返回实体数据对象
- */
- $.httpPost = function (url, param, callback) {
- $.ajax({
- url: url,
- data: param,
- type: "POST",
- dataType: "json",
- async: false,
- cache: false,
- success: function (data) {
- if ($.isFunction(callback)) callback(data);
- },
- error: function (XMLHttpRequest, textStatus, errorThrown) {
- $.httpError(XMLHttpRequest, textStatus, errorThrown);
- },
- });
- },
- /* ajax异步封装:
- * type 请求类型, url地址, param参数, callback回调函数
- * 返回实体数据对象
- */
- $.httpAsync = function (type, url, param, callback) {
- $.ajax({
- url: url,
- data: param,
- type: type,
- dataType: "json",
- async: true,
- cache: false,
- success: function (data) {
- if ($.isFunction(callback)) callback(data);
- },
- error: function (XMLHttpRequest, textStatus, errorThrown) {
- $.httpError(XMLHttpRequest, textStatus, errorThrown);
- },
- });
- };
- })(jQuery);
五、总结
至此,我们发现其实MVC的异常处理,真的很简单,只需要在过滤器中全局注册之后,然后重写OnException的方法,实现逻辑即可。关键是在于项目中Ajax请求,需要用统一的封装方法。
ASP.NET MVC 异常Exception拦截的更多相关文章
- ASP.NET MVC 异常Exception拦截器Fillter
异常信息的处理在程序中非常重要, 在asp.net mvc中提供异常属性拦截器进行对异常信息的处理,异常拦截器也没有什么的,只是写一个类,继承另一个类(System.Web.Mvc.FilterAtt ...
- ASP.NET MVC中的拦截器
在ASP.NET MVC中,有三种拦截器:Action拦截器.Result拦截器和Exception拦截器, 所谓的拦截器也没有什么的,只是写一个类,继承另一个类和一个接口,顺便实现接口里面的方法而以 ...
- ExceptionLess ASP.NET MVC 异常日志框架
Exceptionless 一个开源的实时的日志收集框架,它可以应用在基于 ASP.NET,ASP.NET Core,Web API,Web Forms,WPF,Console,ASP.NET MVC ...
- Asp.Net Mvc - 在OnResultExecut* 拦截Action返回的HTML
在Asp.Net MVC项目中通过重写ActionFilterAttribute中的方法,我们就可以在轻松的在Action方法执行前后做一些特殊的操作如:[身份认证.日志记录.内容截取等]. 但是我们 ...
- ASP.NET MVC的Action拦截器(过滤器)ActionFilter
有时项目要进行客户端请求(action)进行拦截(过滤)验证等业务,可以使用拦截器进行实现,所谓的action拦截器也没有什么的,只是写一个类,继承另一个类(System.Web.Mvc.Filter ...
- ASP.NET MVC 自动模型验证
经常看到这个代码 在controller 中写入验证模型,每个需要验证的action 都写-.. ,就问你烦不烦~ 可以利用 ASP.NET MVC 的 action 拦截机制 自动处理. 1 新建验 ...
- Asp.net mvc 知多少(八)
本系列主要翻译自<ASP.NET MVC Interview Questions and Answers >- By Shailendra Chauhan,想看英文原版的可访问[http: ...
- Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码
本问主要介绍asp.net的身份验证机制及asp.net MVC拦截器在项目中的运用.现在让我们来模拟一个简单的流程:用户登录>权限验证>异常处理 1.用户登录 验证用户是否登录成功步骤直 ...
- ASP.NET MVC案例——————拦截器
摘要 本文将对“MVC公告发布系统”的发布公告功能添加日志功能和异常处理功能,借此来讨论ASP.NET MVC中拦截器的使用方法. 一个小难题 我们继续完善“MVC公告发布系统”, ...
随机推荐
- route命令详解
1. 使用背景:需要接入两个网络,一个是部署环境所在内网环境,这个环境是上不了外网, 外网环境很可能是一个无线网络.如果两者都连接上,很可能导致有一方不能起作用,即外网或内网上不了,常常需要使用繁 ...
- 微信小程序---分包加载(subpackages)及报错
某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载. 分包很简单,具体步骤如下: 在app.json中,创建subPackages,root表示分包A的 ...
- springcloud-知识点总结(一):Eureka
1.Spring Cloud简介 Spring Cloud是一系列框架的有序集合.它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消息总线.负载 ...
- idea常用快捷键及操作
ctrl+j ===== 智能提示 可用模版及关键字 ctrl+p ===== 显示方法可填入的参数 ctrl+space ===== 补全提示项目中可用的变量 ctrl+shift+j ==== ...
- cdnbest里如何查看网站是否被缓存
比如开启了强制缓存,如何查看缓存是否生效 本例以firefox浏览器查看,先打开浏览器,按下F12, 然后在浏览器是输入网址访问 如下图响应头里的 x-cache显示 Miss from 就是没有缓 ...
- NTFS(Windows)、ext4(RHEL6)和xfs(RHEL7)文件系统的误删除恢复和备份
前言 对于误删除文件的设备,要马上停止任何写的操作,防止删除的文件被覆盖,导致数据丢失! 恢复NTFS文件系统下误删的文件 以Windows为例,市面上能恢复的工具不少,例如EasyRecovery. ...
- Linux 禁止普通用户su到root
Linux账户权限管理上为了防止普通用户通过su切换到root用户,需要修改/etc/pam.d/su和/etc/login.defs两个配置文件. Step1:修改 /etc/pam.d/su文件 ...
- f5 主备模式切换
f5 主备模式 主机down自动切换到备 原主机重新启动,自动切换到原主机
- printf 字符串格式化
在将各种类型的数据构造成字符串时,sprintf 的强大功能很少会让你失望.由于sprintf 跟printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出 ...
- 6C - 开门人和关门人
每天第一个到机房的人要把门打开,最后一个离开的人要把门关好.现有一堆杂乱的机房签 到.签离记录,请根据记录找出当天开门和关门的人. Input 测试输入的第一行给出记录的总天数N ( > 0 ...