本文链接: https://www.cnblogs.com/hubaijia/p/about-exceptions-3.html

系列文章:

本文目录

Web Api 的错误返回

在使用.net 的 Web Api构建Endpoint对外提供访问时,往往需要统一的错误返回格式。

如果按照前面两篇文章(一)(二)所说,采用带有ErrorCode的异常ErrorCodeException,那么在WebApi中返回错误时,只要返回ErrorCode即可。

代码如下:

[HttpPut]
[Authorize]
public IActionResult Update(UpdatetNickNameRequest request)
{
try
{
long userId = User.GetUserId(); _identityService.SetNickName(userId, request.NickName); return Ok();
}
catch (IdentityException ex)
{
return new BadRequestObjectResult(ex.ErrorCode);
}
}

ErrorCode类包含了Id, Name, Message属性,方便客户端在收到错误返回后进行处理。

自定义的客户端在接受到Api的返回后,检查HttpStatusCode,如果不成功(不是2xx),那么也直接抛出带有ErrorCode的异常ErrorCodeException即可。

这样,前后端的处理就变得一致和简便。特别是如果你在前端也采用 .net 技术,比如 blazor、xamarin、wpf、winform等等,那前后端可以使用相同的CodeBase。

Exception的捕捉

前面几篇文章,一直在关注Exception的抛出,现在我们来关注一下Exception的捕捉。

那些基础的catch/finally这里就不再赘述,说几个项目中实际出现的现象和问题。

不要吃掉异常

不要吃掉异常有两个层面,一个是直接忽略,比如catch了所有的Exception,并不妥善处理(假处理,就打印一句log,甚至不处理),不过这种失误要不是新手所犯,要不就是编码过程中草草了事的结果。

这里主要注意第二个层面,即catch时不要隐藏异常的类型,即不要catch(Exception ex),这意味着隐藏了异常的类型,吃掉了针对不同的错误采取不同的措施的机会。

微软的code analysis也直接给出了规则 CA1031:不要捕捉一般异常类型.

Checked Exception

CE是确保你所有的异常都得到捕捉,它往往在编译层面提供对Exception的检查,确保你的程序不会因为未处理的异常而终止。

在编写可靠的程序时,程序员需要知道调用的方法抛出什么异常,我是否需要处理,还是包装,还是直接抛出不理,总之我们需要有途径获取这样的信息。

当然,Checked Exception肯定会带来更多的代码量,且在项目初始建立起来,往往需要修改一处,而动整个链条上的代码,一层层的修改。

所以,有些人喜欢CE,认为它带来了规矩,改善了团队代码质量;当然也有些人认为它带来了繁琐。

仁者见仁智者见智,这里不加入这些争辩。争辩1, 争辩2, 争辩3, 争辩4

.net vs java

不得不拿这两门语言进行对比,每个世界都要互相借鉴。

熟悉java语法的同学肯定会知道java方法定义上有throws这一关键词:

public int div(int i,int j) throws Exception {...}

throws这一关键词保证了从方法定义上就能知道一个方法抛出什么样的异常,直接借助编译器检查或者IDE的智能提醒,就不会漏掉异常。

而c#中并没有相同的实现,可以见Stackoverflow上的讨论.

那么在 .net 世界中当你调用一个方法时,怎么妥善的知道这个方法抛出什么样的异常呢?知道后我们才能决定是否处理这个异常,还是继续抛出。

目前的答案是:注释!(不要笑,很严肃的解决方案)。

/// <exception cref="IdentityService.IdentityService">这样写Exception注释</exception>
public void SetNickName(long userId, string newNickName)
{
//....
Ensure.NickNameNotExisted(newNickName);
//....
}

当你翻看.net的源码时,会看到所有方法的注释中都良好的列出了有哪些异常。

有几个问题:

  • 我们团队没人写注释,怎么办?
  • 调用方法时,并没有智能提示有哪些异常,所以我们经常忽略
  • 想看异常就得F12看定义,太繁琐。

有很多问题只从技术上没法解决,但我们尽量可以借助一些Review工具来检查团队的代码,提出要求。

此外你会喜欢上 ctrl + k, ctrl+i 这个快捷键的,他能帮助你快速查看注释文档,查看有哪些异常。

visual studio 扩展推荐

在这里,我推荐一个visual studio的扩展,是的,它的名字就叫 Checked Exceptions, 这是我必备的一款扩展。

这款插件会借助注释的形式,协助实现Checked Exception的功能,并且可以快速添加相应注释。

这个有个小提示:如果你从项目伊始采用这款插件,折磨小一点,如果半路使用,那么当作检验团队代码强健性的工具也不错。

此外,这个款扩展可能还有些bug,导致即使注释了Exception还不断提示,所以我平时并不一直启用它,而是在做Code Review时,使用它作为一个检查工具。

这样可以比较好的解决上面提到的Checked Exception的缺点,又利用它的优点。

常见模式

不要重复的抛出

这个只是简单提醒下,见如下代码。

void BadSmellMethod()
{
try
{
.....
}
catch(Exception ex)
{
//.... some thing // 错误的做法
throw ex;
}
} void GoodMethod()
{
try
{
.....
}
catch
{
//... some thing
// 错误的做法
throw;
}
}

简单来说就是重复抛出,丢失了引发异常原始方法和当前方法调用之间的StackTrace。

在code analysis中也有相应规则。CA2200.

在asp.net core 中

在捕捉异常时,往往一个异常被一路抛出,或者包装再抛出,直到终点。如果到了终点还没有被捕获,那么就会引发程序中止,这是谁都不想看到的。

在asp.net core中,这个终点就是Controller控制器,所以我们需要在Controller的方法里调用需要的Service,然后兜住所有的可能的异常,这也是可以直接捕获Exception的地方。

代码如下:

[HttpPut]
[Authorize]
public IActionResult Update(UpdatetNickNameRequest request)
{
try
{
long userId = User.GetUserId(); _identityService.SetNickName(userId, request.NickName); return Ok();
}
catch (IdentityException ex)
{
// 具体异常
//...others
return new BadRequestObjectResult(ex.ErrorCode);
}
catch (OtherException ex)
{
//具体异常
//... others
return new BadRequestObjectResult(ex.ErrorCode);
}
catch(Exception ex)
{
//这里兜住其他未发现的一切异常
//做好日志
//分析后再优化处理
return new BadRequestObjectResult(ErrorCode.Empty);
}
}

在xamarin.forms中

如果你是同道中人,使用xamarin.forms,那么你肯定知道MVVM模式。

异常的终点往往就在MVVM模式中的ViewModel中,比如LoginPageViewModel中,同样ViewModel调用各项Service,你需要在这里兜住所有可能出现的异常。

结语

本文,简要介绍了具体项目中异常的捕捉问题,欢迎大家交流指正。

下篇,我们关注一下 异步编程中的Exception,以及全局错误处理。

谢谢阅读。

[.net] 关于Exception的几点思考和在项目中的使用(三)的更多相关文章

  1. [.net] 关于Exception的几点思考和在项目中的使用(二)

    本文链接: https://www.cnblogs.com/hubaijia/p/about-exceptions-2.html 系列文章: 关于Exception的几点思考和在项目中的使用(一) 关 ...

  2. [.net] 关于Exception的几点思考和在项目中的使用(一)

    本文链接 https://www.cnblogs.com/hubaijia/p/about-exceptions-1.html 关于exception的基本语法和作用,这里不再赘述,下面记录一下我在项 ...

  3. Android -- 思考 -- 为什么要在项目中使用MVP模式

    1,其实有时候一直在找借口不去思考这个问题,总是以赶项目为由,没有很认真的思考这个问题,为什么我们要在项目中使用MVP模式,自己也用MVP也已经做了两个项目,而且在网上也看了不少的文章,但是感觉在高层 ...

  4. 从有约束条件下的凸优化角度思考神经网络训练过程中的L2正则化

    从有约束条件下的凸优化角度思考神经网络训练过程中的L2正则化 神经网络在训练过程中,为应对过拟合问题,可以采用正则化方法(regularization),一种常用的正则化方法是L2正则化. 神经网络中 ...

  5. VS2013中web项目中自动生成的ASP.NET Identity代码思考

    vs2013没有再分webform.mvc.api项目,使用vs2013创建一个web项目模板选MVC,身份验证选个人用户账户.项目会生成ASP.NET Identity的一些代码.这些代码主要在Ac ...

  6. 由项目中一个hash2int函数引发的思考

    hash2int /** * 计算一个字符串的md5折算成int返回 * @param type $str * @return type */ function hash2int($str) { $m ...

  7. 12月中旬项目中出现的几个bug解决方法的思考

    这周做的项目遇到2个费了很多时间才解决的bug,解决之后,发现根本问题并不是什么很难的技术难点,都是因为自己在写代码的过程中,思维不够清晰.还有一个需要再提高的地方就是解决问题的思维,如何快速定位到问 ...

  8. java Exception 出错的栈信息打印到日志中 打印堆栈信息

    我们在开发程序的过程当中,日志是必不可少的工具,这有助于我们分析问题的原因,和出错的详细信息,而java的异常机制又会方便且迅速的帮我们找到出错行的位置. try { .... } catch (Ex ...

  9. 项目中访问controller报错:HTTP Status 500 - Servlet.init() for servlet spring threw exception

    直接访问controller路径http://localhost:8080/index报错: HTTP Status 500 - Servlet.init() for servlet spring t ...

随机推荐

  1. rem & 16px & 62.5%

    rem & 16px & 62.5% 浏览器的默认字体大小都是16px 兼容性:IE9+,Firefox.Chrome.Safari.Opera 的主流版本都支持了rem 对不支持的浏 ...

  2. Object to Array

    Object to Array objectToArray(obj = {}, title = `标题`){ let datas = []; if(Object.keys(obj).length) { ...

  3. 行业动态 | Apache Pulsar 对现代数据堆栈至关重要的四个原因

    与 Kafka 相比,Pulsar 的架构使它在跨地域复制.扩展.多租户和队列等方面具有重要的优势.   1 月 27 日,DataStax 宣布收购Kesque(Pulsar 即服务),加入到了 P ...

  4. Java 线程间通信 —— 等待 / 通知机制

    本文部分摘自<Java 并发编程的艺术> volatile 和 synchronize 关键字 每个处于运行状态的线程,如果仅仅是孤立地运行,那么它产生的作用很小,如果多个线程能够相互配合 ...

  5. idea将文件push之后如何回退

  6. DRF 视图家族及路由层补充

    目录 视图家族 一.views视图类 1.APIView类 2.GenericAPIView类(generics中) 二.mixins类:视图辅助工具 1.RetrieveModelMixin 2.L ...

  7. 后端程序员之路 46、Redis Sentinel

    Sentinel - Redis 命令参考http://doc.redisfans.com/topic/sentinel.html#sentinel-api Guidelines for Redis ...

  8. Markdown基础使用方法

    Markdown基础使用方法 标题的几种用法 * 选中标题(Ctrl+1~Crtl+6),分别为标题1-6.* #+空格+内容 为一级标题##+空格+内容为二级标题:以此类推. 字体快捷键及使用方法 ...

  9. uniapp封装uni.request请求

    封装一个uniapp请求 新建一个http.js文件封装uni.request const BASE_URL = process.env.NODE_ENV === 'development' ? '' ...

  10. 给新手的 11 个 Docker 免费上手项目

    转: 给新手的 11 个 Docker 免费上手项目 作者:老K玩代码 来源:toutiao.com/i6882755471015576072 Docker 是一个开源的应用容器引擎,让开发者可以打包 ...