在上一篇教程中我为大家介绍了Web API中Filter的开发使用,其中讲到ExceptionFilter时留了一个坑:ExceptionFilter只能截获并处理Action执行过程中发生的异常,在Action执行过程之外如果出现异常,ExceptionFilter是无能为力的。

这些异常包括:

  1、  Controller构造方法中出现的异常

  2、  MessageHandlers中出现的异常

  3、  路由过程中出现的异常

  4、  Body在序列化/反序列化过程中出现的异常

  由此可以看出,ExceptionFilter只能解决ApiControler成功实例化后并执行Action期间出现的异常;为了解决这一个问题,在WEB API中除了ExceptionFilter外还引入了两个针对异常记录、处理的扩展点:

IExceptionLogger 和IExceptionHandler。

而这两个扩展是作为Web API的管道组件进行注册管理的,并且,他们有不同的分工:

IExceptionLogger作为异常日志记录组件,负责异常发生后的日志记录,他贯穿于整个Web API的生命周期中,在Web API框架里,任何一个请求周期中出现任何一个未被捕获/处理的异常都会首先进入这个异常日志记录管道进行异常Log记录,在Web API中可以注册多个IExceptionLogger实例负责不同的异常处理。

IExceptionHandler作为异常处理组件,负责异常发生后的处理工作,他处于异常处理管道的最末端,当IExceptionLogger组件进行一场记录完毕、没有相关的ExceptoinFilter进行异常处理时,才会最终调用ExceptionHandler进行异常处理,在Web API中,有且仅有一个ExceptionHandler进行异常的处理。

在Web API框架中给出了两个基类:ExceptionLogger和ExceptionHandler,在使用ExceptionLogger基类时,他提供了ShouldLog虚方法,该方法在基类中被调用,其作用在于避免同一个异常被同一个ExceptionLogger实例重复记录(如当后续的管道中该异常又被抛出,或者同一个ExceptionLogger对象不小心被注册了两次就会出现重复记录的可能)我们也能复写ShouldLog方法加入我们自己的异常记录判断逻辑以针对不同的场景进行不同的ExceptionLogger调用。如果有兴趣可以反编译一下ExceptionLogger基类看看,他使用了显示接口实现,挺有意思的一个技巧。下面我们来看一个ExceptionLogger使用的例子:

    public class ErroLogger : ExceptionLogger
{
public async Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken)
{
var sb = new StringBuilder();
//获取Log组件
ILogger log = LogManager.GetCurrentClassLogger(); var request = context.Request; sb.AppendLine("URL:");
//获取URL
var url = request.RequestUri.ToString();
sb.AppendLine(url); log.Error(context.Exception,sb.ToString(),"");
} public override bool ShouldLog(ExceptionLoggerContext context)
{
return context.Exception is DemoException && base.ShouldLog(context);
}
}

在这个例子中,我们重写了ShouldLog,保证了这个ExceptionLogger只记录DemoException这个类型的异常,并且也调用了基类方法,保证不会重复记录同一个异常。在LogAsync方法中,我通过Log组件记录了导致异常的请求URL,也记录了异常信息。

接下来我们要对这个组件进行注册:

在App_Start/WebApiConfig.cs文件中的Register方法中写入

config.Services.Add(typeof(IExceptionLogger),new ErroLogger());

这样,一个针对DemoException的异常记录组件就开发完成并注册完成了,当Web API执行管道中出现未处理的DemoException异常,均会调用则个组件进行记录。

接下来我们来写一个ExceptionHandler,在整个Web API框架中,ExceptionHandler只能提供一个实例,与ExceptionLogger一样,我们可以继承ExceptionHandler基类来简化异常处理,在ExceptionHandler中也提供了ShouldHandle方法来判断该异常是否应该处理,避免重复处理管道中其他环节重复抛出的异常。我们也同样提供一个例子:

    public class ErrorHandler : ExceptionHandler
{
public override async Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
if (context.Exception is DemoException)
{
context.Result = new ResponseMessageResult(context.Request.CreateResponse(HttpStatusCode.BadRequest,new {Message=context.Exception.Message}));
}
else
{
context.Result = new ResponseMessageResult(context.Request.CreateResponse(HttpStatusCode.InternalServerError,new {Message = "服务器已被外星人绑架"}));
}
}
}

在这个例子中,我们判断了异常的类型,并根据不同的异常返回客户端不同的响应内容和不同的HTTP状态码。

然后在配置中注册这个异常处理模块,在App_Start/WebApiConfig.cs文件中的Register方法中写入

config.Services.Replace(typeof(IExceptionHandler),new ErrorHandler());

这样就替换了系统默认的ExceptionHandler,可以使用我们自定义的Handler进行异常的处理了。

在异常记录、处理过程中,我们都碰到相应的异常上下文参数,我们能通过这个参数获取当前请求的上下文,获取请求、响应(小心有时会为空哦)、捕获到该异常的catch块信息等内容,我们可以利用这些信息更好地描述、记录、处理异常。

到这里ExceptionLogger组件和ExceptionHandler组件简单的开发就完成了。在开发的过程中我们可以看到,ExceptionLogger负责了全局的异常记录,在Web API框架管道下出现未处理的异常ExceptionLogger都会进行捕获、记录。而ExceptionHandler和ExceptionFilter功能是有重叠的,那何时使用ExceptionHandler何时使用ExceptionFilter呢?我们可以将两者的区别列出如下的表格:

ExceptionFilter

ExceptionHandler

作用域

Controller、Action

全局

实例个数

无限制

全局唯一

作用条件

Controller实例化成功之后

Web API成功加载之后

  经过上面的表我们可以看出,如果处理颗粒度细致到Controller、Action级别时,ExceptionFilter处理起来会更得心应手,他已经能精确定位到某个Action,然后可以针对当前Action做定制开发。而ExceptionHandler作用域远大于ExceptionFilter,他处理全局更有优势。

  关于Web API异常处理的相关内容我就讲这么一些,如果在文章中有任何不正确的地方或者疑问,欢迎大家为我指出。

 
 

Web API系列(三) 异常处理的更多相关文章

  1. Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群

    Redis总结(五)缓存雪崩和缓存穿透等问题   前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...

  2. Web API系列(三)统一异常处理

    前面讲了webapi的安全验证和参数安全,不清楚的朋友,可以看看前面的文章,<Web API系列(二)接口安全和参数校验>,本文主要介绍Web API异常结果的处理.作为内部或者是对外提供 ...

  3. Web API系列(二)接口安全和参数校验

    以前简单介绍过web api 的设计,但是还是有很多朋友问我,如何合理的设计和实现web api.比如,接口安全,异常处理,统一数据返回等问题.所以有必要系统的总结总结 web api 的设计和实现. ...

  4. [转]ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)

    本文转自:http://www.cnblogs.com/parry/p/ASPNET_MVC_Web_API_digest_authentication.html 在前一篇文章中,主要讨论了使用HTT ...

  5. ASP.NET Web API(三):安全验证之使用摘要认证(digest authentication)

    在前一篇文章中,主要讨论了使用HTTP基本认证的方法,因为HTTP基本认证的方式决定了它在安全性方面存在很大的问题,所以接下来看看另一种验证的方式:digest authentication,即摘要认 ...

  6. ASP.NET Web API系列教程目录

    ASP.NET Web API系列教程目录 Introduction:What's This New Web API?引子:新的Web API是什么? Chapter 1: Getting Start ...

  7. 【ASP.NET Web API教程】4.3 ASP.NET Web API中的异常处理

    原文:[ASP.NET Web API教程]4.3 ASP.NET Web API中的异常处理 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本系列教程,请先看前面的内 ...

  8. Web API系列之三 基本功能实现

    Web API系列之二讲解了如何搭建一个WebApi的基架,本文主要在其基础之上实现基本的功能.下面开始逐步操作: 一.配置WebApi的路由-用于配置外部如何访问内部资源的url的规则 1.添加Gl ...

  9. ASP.NET Web API系列教程(目录)(转)

    注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP.NET Web API.这是一个用来在.NET平台上建立HTTP服务的Web API框架,是微软的又一项令人振奋的技术.目前,国内 ...

  10. [转]ASP.NET Web API系列教程(目录)

    本文转自:http://www.cnblogs.com/r01cn/archive/2012/11/11/2765432.html 注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP ...

随机推荐

  1. 虚拟机中linux系统的安装教程

    虚拟机是什么? 虚拟机(Virtual Machine)是指一种特殊的软件,可以在计算机和用户之间创建一种环境,用户可以用这个软件所创建的环境来操作.虚拟机就像像真实机器一样运行程序,满足用户的需求. ...

  2. 17.结构体(typedef)

    1.结构体 a.结构体类型定义b.结构体变量定义c.结构体变量的初始化d.typedef改类型名e.点运算符和指针法操作结构体f.结构体也是一种数据类型,复合类型,自定义类型 2.结构体变量的定义 ( ...

  3. 常用sql 集合记录整理

    select 'truncate table ' + Name + ';' from sysobjects where xtype='U' order by name asc; -- 查询出指定库的 ...

  4. ThinkPHP设计模式与Trait技术

    阅读原文 设计模式 单例模式 class Site { //属性 public $siteName; //本类的静态实例 protected static $instance = null; //禁用 ...

  5. git提交代码时,Unstaged changes如何过滤.class .log等文件

    在项目下创建一个.gitignore文件,内容如下: 可以在文件目录中加入这个文件,也可以在eclipse中项目下加入此文件 /target/表示忽略target文件夹下的内容 .class 表示忽略 ...

  6. ionic3 小记录

    cordova platform add ios@latest 安装最新ios ionic cordova build ios -- --buildFlag="-UseModernBuild ...

  7. Hadoop组件

    ---------Hive--------------------------zooKeeper-------------------------------kafka---------------- ...

  8. PeopleSoft 单点登录

    第一次会以guest 用户进来,code 为空 第二次也以guest 进来code 从ssoAP获取到code,根据code 获取token,根据token 获取用户id.

  9. Unity如何退出游戏

    使用 Application.Quit(),但在 editor 模式下使用 Application.Quit()是没用的,要用 EditorApplication.isPlaying = false. ...

  10. RoboMongo命令(版本:Robo 3T 1.1.1)

    查询并排序db.getCollection('working_history').find({"identification" : "76170178"}).s ...