前言:最近在某个项目里面遇到一个有点纠结的小问题,经过半天时间的思索和尝试,问题得到解决。在此记录一下解决的过程,以及解决问题的过程中对.net里面MVC异常处理的思考。都是些老生常谈的问题,不多说,直接上“主菜”。

本文原创地址:http://www.cnblogs.com/landeanfen/p/8135844.html

一、问题重现

项目是一个传统.net framework的MVC项目,为了简便,项目里面定义了一个自定义异常类用于向客户端传递错误消息,客户端接收到异常的消息时在浏览器里面弹出提示。先来看看这个自定义异常类CustormerException的定义

    public class CustomerException : System.Exception{public CustomerException(){}public CustomerException(string message) : base(message){}public CustomerException(string message, params object[] args) : base(string.Format(message, args)){}}

为了模拟重现问题,我尽量将代码简化再简化。

  [BaseException]public class DefaultController : Controller{// GET: Defaultpublic ActionResult Index(){return View();}public JsonResult Login(string userName, string password){if (userName == "admin" && password == "admin"){return Json(true, JsonRequestBehavior.AllowGet);}else{throw new CustomerException("用户名或者密码错误");}}}public class BaseExceptionAttribute : HandleErrorAttribute{public override void OnException(ExceptionContext filterContext){if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception is CustomerException){filterContext.ExceptionHandled = true;filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;var result = new ContentResult() { Content = filterContext.Exception.Message, ContentType = MediaTypeNames.Text.Plain };filterContext.Result = result;}else{//记录日志
            }base.OnException(filterContext);}}

代码不复杂,就是一个通用的异常过滤器,用于记录日志和传递消息到客户端。

然后我们看看客户端的测试代码:

@{ViewBag.Title = "Index";
}用户名:<input type="text" id="username"/>
密码  :<input type="text" id="password"/>
<button id="btnAjaxError" type="button">登陆</button>
@section Script
{<script type="text/javascript">$(function () {$("#btnAjaxError").click(function () {$.ajax({url:"/Default/Login",data: { userName: $('#username').val(), password: $('#password').val() },type: 'post'}).done(function (data) {console.log("successful:"+data);}).fail(function (a, b, c) {debugger;console.log("fail:"+a.responseText);});});});
</script>
}

本地调试、运行得到正常结果

发布到IIS,本地访问仍然正常。可是当我们远程访问的时候问题出现了。

所有的远程访问机器上面都出现了系统默认的错误消息,而不是我们返回的业务异常消息。

二、初次尝试

对于这种本地能看到详细异常,而远程看不到详细异常的问题,相信有一定经验的朋友肯定想到了一个配置,那就是Web.config里面的CustomErrors节点,我们配置下默认开启自定义异常不就行了吗。嘿嘿!就是这么简单!博主当初也是这么乐呵呵的去尝试的。我们在Web.config的System.web节点下面加入这个节点

<customErrors mode="On"></customErrors>

可是很遗憾,问题依旧!后来想是不是自己对于On、Off、RemoteOnly的理解有误?于是乎三个项逐个尝试,结果均已失败告终!

于是乎开始有点郁闷了,这种问题原来怎么没遇到过了,代码“貌似”没什么大问题啊,如果有问题,本地应该也不能得到才对啊。于是乎分析,这可能不是我们代码的问题,而是IIS给我做了一层统一的异常处理,我们只需要将这层统一的异常处理去掉就行了啊。道理是这么个道理,可是如何去实现呢。于是乎把IIS的各个功能都试了个遍,最后的谷歌的一篇帖子里面找到了一些帮助。解决方案如下。

三、解决方案

1、“不是代码的问题”的解决方案

上文说到这个问题或许不是代码的问题,而是IIS配置的问题。于是乎真的让博主找到了解决方案。解决步骤如下:

原来,IIS默认是不让远程用户查看异常的详细错误的,如果是远程用户,IIS会默认给你返回一个各种状态码对应的默认消息,我们自定义的消息将会被此覆盖。如果改成选中第二项,就表示不管是本地用户还是远程用户均可以看到详细异常。

这样配置之后不用更改任何代码,不用理会是否配置了CustomErrors节点,远程用户均可以正常获取到程序返回的异常消息:

2、“是代码的问题”的解决方案

有了上面的解决方案,为何还会有“是代码的问题”的解决方案呢?这才是本文想要表达的中心思想。既然我们通过配置IIS的错误页可以解决这个问题,那么我们为什么不能在程序的范畴内去解决呢?博主是一个有点喜欢刨根问题的人,不断分析代码后发现,既然系统的默认错误消息可以覆盖我们的自定义异常消息,那么反过来,我们自定义的异常消息为什么就不能覆盖系统默认的异常消息呢?于是乎发现在重写父类的OnException方法的时候,上面的代码我们是先执行的我们自定义的异常消息,然后再调用 base.OnException(filterContext); 去执行系统默认的异常消息处理的,那么我们将这个顺序倒置一下,反过来是不是可行呢?于是代码就变成了这样:

  public class BaseExceptionAttribute : HandleErrorAttribute{public override void OnException(ExceptionContext filterContext){base.OnException(filterContext);if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception is CustomerException){filterContext.ExceptionHandled = true;filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;var result = new ContentResult() { Content = filterContext.Exception.Message, ContentType = MediaTypeNames.Text.Plain };filterContext.Result = result;}else{//记录日志
            }}}

我们将上面通过配置IIS错误页的解决方案还原,改成默认的配置。去掉CustomErrors节点,重新发布之后,问题完美解决:

问题能解决,说明博主上面的推想或许是正确的,自定义异常和默认异常是存在一个先后顺序的,我们如果要覆盖系统的异常,需要将我们自定义异常的代码放在后面执行。这个论断是通过上述解决问题的思路推理得来的,并不一定正确,有兴趣的可以反编译下dll看下是否真是这样!

很有趣的一点就是,这样改了代码之后,我们如果在web.config里面加入customErrors节点,并且将mode设置为Off,远程访问的时候得到的异常消息又变成了“错误的请求”。其实这不难理解,当你禁用自定义错误信息,那么系统肯定会给你返回默认的异常信息了。

四、总结

由上述的两种解决方案可以看出这里其实有三道防线:

第一道防线是最外层的防线,就是IIS的错误页配置,如果这层配置选择的是详细错误,那么不管你其他的配置是什么样,都会返回用户自定义的错误信息;

第二道防线是中间的那层,就是web.config里面的CustomErrors节点,如果第一道防线是默认配置,这层防线才会生效;

第三道防线才是代码的范畴,这个受限于CustomErrors节点的配置。

本文原创出处:http://www.cnblogs.com/landeanfen/

欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利

MVC系列——一个异常消息传递引发的思考相关推荐

  1. 一个分组查询引发的思考

    一个分组查询引发的思考 我们在看项目代码或者SQL语句时, 往往会看到很多非常复杂的业务或者SQL 那么问题来了. 复杂SQL是如何写成的? 下面通过一个数据展示的需求来体会到复杂的SQL是如何书写的 ...

  2. 一个小程序引发的思考

    既然是一个小程序引发的思考,那么我们就先看看这个小程序,看看他有何神奇之处: namespace ConsoleApplication1 {class Program{static void Main ...

  3. 热备双机中备机处理业务异常故障引发的思考

       突然想起昨天某业务主管跟我讨论问题"主备热备双机情况下,备机的业务程序如何保障正确性".这个问题源于某业务系统是有主备热备双机,但备机平时处于等待状况,当主机有异常时,才使用 ...

  4. 一次线上Redis类转换异常排查引发的思考

    之前同事反馈说线上遇到Redis反序列化异常问题,异常如下: XxxClass1 cannot be cast to XxxClass2 已知信息如下: 该异常不是必现的,偶尔才会出现: 出现该异常后 ...

  5. 一个小需求引发的思考

    此文已由作者肖凡授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 最近接了一个看上去很小,很容易实现的需求,然而在做的过程中发现有些问题如果思考不全就很容易导致问题,这里给大家 ...

  6. 一个mysql事务引发的思考(血案)

    #问题简述 大家都知道mysql是支持ACID,支持事务的,事务是非常重要的一个特性,要不都执行成功,要不都不成功.我们在coding时也会大量用到,但是随着业务代码的累加与使用人数的增加,系统有某些 ...

  7. 一个嵌入式初学者引发的思考(jesse谈自己的经验体会) 【转】

    我目前再跟几个朋友合伙一起做点开源的硬件小产品,随后就成立了一个论坛,也就是现在的armjishu.com,那时候我们在一起商量着怎么让我们的广大初学者能够更快的进入到嵌入式领域,我们琢磨了很长时间, ...

  8. 一个mss大小引发的思考

    最近收到一个某地区拨打电话失败的概率性问题,据测试部门的同事反馈,发生的概率为 1/10.即拨打10次电话,基本上就会出现一次,出现问题后,再次拨打又好了,没啥规律,没有必现路径,真是奇怪了.每次遇到 ...

  9. 一个播放器引发的思考——谈谈React跨组件通信

    在我们react项目日常开发中,往往会遇到这样一个问题:如何去实现跨组件通信? 为了更好的理解此问题,接下来我们通过一个简单的栗子说明. 实现一个视频播放器 假设有一个这样的需求,需要我们去实现一个简 ...

最新文章

  1. TikTok跨境出海:Tiktok怎么月入几十W?
  2. 【每日SQL打卡】​​​​​​​​​​​​​​​DAY 15丨查询活跃业务【难度中等】
  3. 在Windows上删除所有的Oracle安装 和电脑名改变后的设置...
  4. abp vnext修改密码策略
  5. imooc数据结构探险-栈篇 栈应用括号匹配二 由群友启发改良james_yuan老师算法
  6. Matlab的语言概述
  7. VM14 15 pro Mac更改最大分辨率
  8. 不择手段背单词、新东方词根词缀词典、超级新华字典、英语词根词缀记忆大全词典
  9. MyBatis开发文档
  10. SRIO学习笔记之SRIO简介与Xilinx SRIO ip核例程详解
  11. 在线画图工具(流程图、E-R图等)
  12. 华为机试:九宫格按键输入法
  13. excel随机数_Excel生成随机数、不重复随机数技巧,试验检测办公必备
  14. anywhere 无法正常使用的问题--IP地址解析
  15. Fluent——UDF监测指定点的物理量
  16. vmvare打开虚拟机时报错:vmx文件已损坏
  17. Rational Rose的讲解
  18. python 人民币兑美元汇率代码_人民币对美元汇率的大数据分析与预测【完整代码】...
  19. 北京PM2.5浓度回归分析
  20. 计算机二级C语言中isdigit,C使用带字符串的标准算法,带有isdigit的count_if,函数转换...

热门文章

  1. Windows2003无法看到网络连接,修复COM+组件
  2. 模块开发卷宗是什么_详论单片机固件模块化架构设计(精华)
  3. Python3|Opencv——添加高斯噪声Gauss Noise
  4. 24.下拉列表的交互事件
  5. Unity2017.1官方UGUI文档翻译——Scrollbar
  6. 洛谷 [P1801] 黑匣子
  7. 一个简单遮罩弹窗效果
  8. win7 web开发遇到的问题-由于权限不足而无法读取配置文件,无法访问请求的页面...
  9. IBM发明世界首个人造神经元,人工智能的底层硬件基石已完成!
  10. Fast RCNN 训练自己数据集 (2修改数据读取接口)