【WCF】自定义错误处理(IErrorHandler接口的用法)
当被调用的服务操作发生异常时,可以直接把异常的原始内容传回给客户端。在WCF中,服务器传回客户端的异常,通常会使用 FaultException,该异常由这么几个东东组成:
1、Action:在服务调用中,action标头比较重要,它是塞在SOAP消息的Headers元素下面的,是消息头的一部分,action用来对服务操作进行定义的。用小学生能听懂的话说,就是某个服务操作的“学号”,通道层在消息调度时,会根据它来寻找要调用的Operation。记得老周举过例子,就好比你去王老板家里,你得知道王老板住在哪个窝里面。
2、FaultCode:姑且翻译为SOAP错误码吧,虽然叫“码”,但它并不是纯数字表示的,与HTTP Error Code不同的。HTTP错误码是个数字,比如妇孺皆知的404错误。SOAP错误码实际上是一个叫Fault的元素,它塞在Body中,Fault元素下面有个叫Code的元素,就是这个FaultCode对象了,其实这个玩意儿你可以不用手动去定义它的,为啥呢,待会儿告诉你。
3、FaultReason:主要用来指定描述SOAP错误的自定义文本,表示发生错误的原因,其实这个东西在许多时候你也不必手动定义,原因待会儿再说。不过,这个Reason可以指定不同语言版本的错误信息,比如中文的,鸟语的。比如这样:
<Reason>
<Text xml:lang="zh-CN">此错误的创建者未指定“原因”。</Text>
</Reason>
zh-cn表示简体中文,你可以指定其他物种的语言,如龟语、猫语、兔崽子语种等。
不过,有时候,FaultException用起来不够爽,于是,类库又提供了一个从FaultException派生的类——FaultException<TDetail>,注意它有个泛型参数,这个类的亮点在于,你可以用某个类型来封装你的错误信息,然后把那个类型定义为数据协定。数据协定懂吧,就是一个类,并且可以XML序列化。如果不能XML序列化,那怎么把它的内容塞进SOAP消息中呢,别忘了,WCF是基于SOAP消息通信的(当然它可以像Web API那样,用JSON/XML通信),为了让对象可以在不同的端之间传递,当然得支持XML序列化了,对吧。就像你要寄快递,你不能叫快递员上门取炸弹,因为炸弹是禁止快递的,所以你必须寄允许的物品,这样物流才能流通。
如果你的错误信息比较简单,比如string、double、int这些,属于基本类型,那你不用定义数据协定了,因为基础类型是可以序列化的。例如,FaultException<int>。
好了,上面一堆F话,其实是为了让大伙认识一下FaultException,因为它很帅,也很有用,后面例子会用到它。
下面介绍一下 IErrorHandler 接口,来,先看看它的长相。
public interface IErrorHandler
{ bool HandleError(Exception error); void ProvideFault(Exception error, MessageVersion version, ref Message fault);
}
长得不是很清秀,将就一点吧,代码的颜值都差不多,C#的长相算是比较优雅的了,要是JS的话,估计代码都看得你头晕。
这个接口的作用就是用来给咱们扩展WCF的错误处理的,实现这个高大上的接口,我们可以自定义返回给客户端的错误消息。接口不是很复杂,只有两个方法,标准的二胎:
1、HandleError:方法参数是服务操作引发的异常,在这个方法里,你可以通过方法参数拦截这个异常。比如,你可以在这里实现自己的错误日志记录。如果你已经处理了错误,应该让方法返回true,这样运行时知道你已经处理了,就不再往下抛异常了,不然,很有可能导至服务马上挂掉(单实例模式除外)。
2、ProvideFault:这个方法相当有用,在这个方法里面,你可以自己根据需要产生一条发回客户端的消息,当然是带Fault元素的消息,因为它不是正常消息,是错误消息。方法有三个参数:
1)error:服务操作抛出来的原始异常,这个能理解吧。
2)version:SOAP消息需要的版本,随后你产生Fault消息时要用到它。
3)fault:这是核心,Message,表示错误消息实例。方法被调用时,这个参数是null的,因此,在方法结束前,你必须给它赋值一条Message,就是因为这样,这个参数才声明为 ref。
实现这个接口后,你得想办法把它放进 ChannelDispatcher 类的 ErrorHandlers 集合中,这个类其实你看它那名字就猜到,它是负责调度通道层的。
在WCF中,99.9957%的扩展都通过扩展 Behavior 来实现的,而服务的每个层面上都有各自的 behavior ,比如,IServiceBehavior、IEndpointBehavior、IContractBehavior、IOperationBehavior ……
至于说应该从哪个behavior扩展,没有什么硬性规定,一切视实际应用而定,这很灵活,你知道的,世界上唯一不变的就是变化,学习编程不是背九九乘法表。
由于通道层与服务协定在正常情形下是对应的,而且在客户端,是可以直接将服务协定(Service Contract)当成通道来用的,WCF内部会有默认的实现来完成这些转化,当然你有本事的话也可以自己写通道。一般也没多大必要,因为我们常用的HTTP,TCP之类的通信协议,默认都有了,反正老周从没写过通道层。唉,老周水平低下,只能玩点简单的东西,玩复杂的东西不行。
于是,老周今天提供的例子,是从服务协定的behavior来扩展,为了方便用,我还把它写成Attribute,这个老周在前面的文章中说过的,写成Attribute的扩展,WCF运行时也能自动识别,并插入对应的Behaviors集合中。
这个扩展稍后再扯,先扯重点,就是实现IErrorHandler接口。
直接上代码,不难。
public class ServiceErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
} public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
FaultException<string> fex = new FaultException<string>(error.Message);
MessageFault mf = fex.CreateMessageFault();
fault = Message.CreateMessage(version, mf, "http://zh-ja-demo/svfault");
}
}
这里老周不打算处理异常,所以HandleError直接返回true就行了,省事省代码省时间。
因为错误消息不是很复杂,老周就选用简单的FaultException<string>,以字符串来包装错误的详细信息。然后调用FaultException<string>的CreateMessageFault 方法就能够得到一个 MessageFault 实例,有了这个 MessageFault 实例,就可以直接创建 Message 了。
还记得吗,在前文中,介绍FaultException时,老周说过,FaultCode和FaultReasion可以不用自己来定义的,答案就在这里了,一个 CreateMessageFault 方法就全包了,省事省力。
最后,不要忘了,用 CreateMessage 方法创建一条发回客户端的消息对象。
请大家记得,这个错误消息的 action 是:
http://zh-ja-demo/svfault
好像有点难记,老周也后悔了,干吗用这么屌的 action 名字。为啥要注意action呢,因为虽然它是一条错误消息,可是那也是一条SOAP消息,是吧,既然是SOAP消息,它必须一个action头来定位它要调用的操作,这个操作不是协定操作,而是让客户端能够收到这条错误消息,如果没有action,客户端可能定位不到这条消息,当然不是绝对情况,这里面的事情很复杂,说不清。
这个 action 后面会用到。
接下来,扩展一下协定层的behavior。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
public class MyContractBehaviorAttribute : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{ } public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
} public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
ServiceErrorHandler sverr = new ServiceErrorHandler();
dispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(sverr);
} public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
}
因为这个只是处理服务器上的错误,不处理客户端,所以ApplyClientBehavior方法留空,不用管它。
然后把这个Attribute应用到协定接口或者服务类上即可,此处我应用到服务类上,反正不用向客户端公开。
[MyContractBehavior]
class DemoService : IDemo
{
……
现在,我们在实现服务的操作中引发一下异常。
public long RunWork(int bs)
{
if (bs < )
{
throw new ArgumentException("参数不能小于0。");
}
if(bs > )
{
throw new ArgumentException("参数不能大于1000000。");
} long res = 0L;
int k = ;
while(k <= bs)
{
res += k;
k += ;
}
return res;
}
这代码的意思很简单,我就不解释了。
现在,在客户端调用一下服务。
ChannelFactory<Contracts.IDemo> fac = new ChannelFactory<Contracts.IDemo>("client_ep");
Contracts.IDemo dmchannel = fac.CreateChannel();
try
{
long r = dmchannel.RunWork(baseNum);
tb.Text = $"计算结果:{r}";
}catch(FaultException<string> fex)
{
MessageBox.Show(fex.Detail);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
fac.Close();
如下图,调用时,故意输入一个不符合要求的值,让服务器引发异常。
很遗憾的是,没有出现我们自定义的错误提示。
那是因为服务器没有把错误消息回发给客户端就把连接关闭了。
要排除这个问题也是TMD简单,只需要在服务操作协定上加上这个Attribute即可。
[FaultContract(typeof(string), Action = "http://zh-ja-demo/svfault")]
long RunWork(int bs);
注意,FaultContractAttribute是应用在协定方法上的。
传给构造函数的Type一定要与FaultException<TDetail>中的泛型匹配,记得吧,刚刚我们用的是string,所以这里也要用string。刚才在实现IErrorHandler时,老周说过,那个错误消息的 action 你要记住,因为这里要用,Action 所指定的值必须和我们刚刚在ErrorHandler中定义的action相匹配,否则客户端找不到错误消息。
好好,加了这个Attribute后,问题就解决了,此时,就能显示服务器上的错误消息了。
OK,本文的内容就扯完了,该开饭了。
【WCF】自定义错误处理(IErrorHandler接口的用法)的更多相关文章
- 【WCF】错误处理(四):一刀切——IErrorHandler
前面几篇烂文中所介绍到的错误方式,都是在操作协定的实现代码中抛出 FaultException 或者带泛型参数的detail方案,有些时候,错误的处理方法比较相似,可是要每个操作协定去处理,似乎也太麻 ...
- 自定义错误页面mvc用法
原谅我这个新手,对大神们来说这么简单的问题,竟折腾了我一个上午,仅此文章做个记录,供以后备用. 自定义错误页面(custom error pages)在asp.net webform里的配置请看htt ...
- redirect的错误用法asp.net怎么使用自定义错误
工作了几年,写过程序也运营过网站,自定义错误也很熟悉了,最近再做项目发现有同事写了这样的代码 if (action != null) { id = Request.QueryString[" ...
- 【WCF】错误协定声明
在上一篇烂文中,老周给大伙伴们介绍了 IErrorHandler 接口的使用,今天,老周补充一个错误处理的知识点——错误协定. 错误协定与IErrorHandler接口不同,大伙伴们应该记得,上回我们 ...
- 【WCF】错误处理(三):错误协定
最近折腾换电脑的事,博客就更新慢了点.好,不废话,直入正题. 前面老周介绍过,SOAP消息中的错误信息是用一个 Fault 元素来包装的,前面老周也讲了其中的 FaultCode 元素,即可以对错误信 ...
- PHP错误处理函数set_error_handler()的用法
定义和用法 set_error_handler() 函数设置用户自定义的错误处理函数. 该函数用于创建运行时期间的用户自己的错误处理方法. 该函数会返回旧的错误处理程序,若失败,则返回 null. 语 ...
- 【WCF】错误处理(一):FaultException 与 FaultReason 的搭配
这里所说的错误处理主要是指服务代码中抛出的异常,即开发人员主动抛出的错误当然,由于网络问题或者配置不正确,会引发连接超时的错误,但这里老周要说的是,我们在实现服务逻辑时主动抛出的异常,尤其是对客户端传 ...
- ASP.NET MVC-异常处理&自定义错误页
一.应用场景 对于B/S应用程序,在部署到正式环境运行的过程中,很有可能出现一些在前期测试过程中没有发现的一些异常或者错误,或者说只有在特定条件满足时才会发生的一些异常,对于使用ASP.NET MVC ...
- .net自定义错误页面实现升级篇
问题描述: 在上一篇博文 ".net自定义错误页面实现" 中已经介绍了在.net中如何实现自定义错误页面实现(有需要者可以去上一篇博文了解),单纯按照上一篇博文那样设置,能够实现所 ...
随机推荐
- Apache执行Python脚本
由于经常需要到服务器上执行些命令,有些命令懒得敲,就准备写点脚本直接浏览器调用就好了,比如这样: 因为线上有现成的Apache,就直接放它里面了,当然访问安全要设置,我似乎别的随笔里写了安全问题,这里 ...
- ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之第二章:利用模型类创建视图、控制器和数据库
在这一章中,我们将直接进入项目,并且为产品和分类添加一些基本的模型类.我们将在Entity Framework的代码优先模式下,利用这些模型类创建一个数据库.我们还将学习如何在代码中创建数据库上下文类 ...
- 我是如何在SQLServer中处理每天四亿三千万记录的
首先声明,我只是个程序员,不是专业的DBA,以下这篇文章是从一个问题的解决过程去写的,而不是一开始就给大家一个正确的结果,如果文中有不对的地方,请各位数据库大牛给予指正,以便我能够更好的处理此次业务. ...
- Unity 序列化
Script Serialization http://docs.unity3d.com/Manual/script-Serialization.html 自定义序列化及例子: http://docs ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(66)-MVC WebApi 用户验证 (2)
系列目录 前言: 回顾上一节,我们利用webapi简单的登录并进行了同域访问与跨域访问来获得Token,您可以跳转到上一节下载代码来一起动手. 继续上一篇的文章,我们接下来演示利用拿到的Token来访 ...
- Android权限管理之RxPermission解决Android 6.0 适配问题
前言: 上篇重点学习了Android 6.0的运行时权限,今天还是围绕着Android 6.0权限适配来总结学习,这里主要介绍一下我们公司解决Android 6.0权限适配的方案:RxJava+RxP ...
- php注册审核
通过注册审核,判断刚创建的账户是否可以使用. 后台管理员审核通过后,账号可以使用. 通过session 设置只能通过登录入口进入网页. 原理:通过数据库设置账号的一个字段状态,例: isok:1, i ...
- 如何使用swing创建一个BeatBox
首先,我们需要回顾一些内容(2017-01-04 14:32:14): 1.Swing组件 Swing的组件(component,或者称之为元件),是较widget更为正确的术语,它们就是会放在GUI ...
- ObserverPattern(观察者模式)
import java.util.ArrayList; import java.util.List; /** * 观察者模式 * @author TMAC-J * 牵一发而动全身来形容观察者模式在合适 ...
- BPM生产安全管理解决方案分享
一.方案概述生产安全管理是企业生产管理的重要组成部分,组织实施好企业安全管理规划.指导.检查和决策,保证生产处于最佳安全状态是安全管理的重要内容和职责.H3 BPM企业生产安全管理解决方案是一套专门为 ...