这里所说的错误处理主要是指服务代码中抛出的异常,即开发人员主动抛出的错误当然,由于网络问题或者配置不正确,会引发连接超时的错误,但这里老周要说的是,我们在实现服务逻辑时主动抛出的异常,尤其是对客户端传入的参数的验证上面。

WCF的异常信息一般会通过 FaultException 类来包装。理论和概念性的东西,大家可以去查资料,老周向来不喜欢谈那些,下面咱们通过实例来了解一下 FaultException。

定义服务协定。

    [ServiceContract(Namespace = "demo-app")]
public interface IOrder
{
[OperationContract]
bool NewOrder(DateTime date, decimal price, long q);
}

假设这个服务的功能是用来下单的,当然不是真的实现下单功能,因为那样太复杂,也不是本文的重点,这里老周安排了三个参数,分别表示下单日期,商品单价,以及商品数量。

下面,咱们来实现一下这个协定。

    internal class OrderSvr : IOrder
{
public bool NewOrder(DateTime date, decimal price, long q)
{
// 验证
if (date < DateTime.Now)
{
throw new ArgumentException("至少要在今天下单");
}
if (price < || q < )
{
throw new ArgumentException("单价或数量不能小于0");
}
// 模拟下单
if ((q * price) == 0.00M)
{
return false;
}
return true;
}
}

正如大伙所看到的,我在实现的服务方法中对参数进行了验证,假设日期早于今天就会抛出异常。

下面是配置文件中的配置信息,这是演示,没什么安全性要求,就使用基本的 HTTP 通信可以了。

  <system.serviceModel>
<services>
<service name="TestApp.OrderSvr">
<endpoint contract="TestApp.IOrder" binding="basicHttpBinding" address="http://127.0.0.1:6035/order"/>
</service>
</services>
<client>
<endpoint name="epcl" contract="TestApp.IOrder" binding="basicHttpBinding" address="http://127.0.0.1:6035/order"/>
</client>
</system.serviceModel>

为了使客户端调用起来舒坦,我还封装一个客户端类(你可以通过服务引用来让VS自动生成,这里为了装逼,我就手动写)。

    public class SampleClient : ClientBase<IOrder>, IOrder
{
public SampleClient() : base("epcl") { } public bool NewOrder(DateTime date, decimal price, long q)
{
return Channel.NewOrder(date, price, q);
}
}

好D,服务的大致功能就是这样,下面我们来调用一下,顺便,咱们传递不符合要求的参数值,看看客户端能否获取到服务器上抛出的异常。

            SampleClient client = new SampleClient();
try
{
bool r = client.NewOrder(date, price, q);
MessageBox.Show(r ? "下单成功。" : "下单失败。");
}
catch(FaultException ftex)
{
MessageBox.Show(ftex.Reason.GetMatchingTranslation().Text);
}
finally
{
client.Close();
}

WCF的异常使用 FaultException 来封装,然后通过 SOAP 消息发回给客户端,所以此处咱们应当捕捉 FaultException 类型的异常。

测试参数如下图所示。

日期晚于今天,符合,但是数量小于0,不符合。故,调用服务后,会看到如下面的高清无码图片所示的异常信息。

噢,your god,大伙发现,没有出现我们想看到的自定义异常信息。

依据上面的提示,可以开启 IncludeExceptionDetailInFaults 选项,有两种方式可以开启该选项,大伙任选其一即可。

1、使用 ServiceBehaviorAttribute,这个特性要应用到实现服务协定的类上,比如,咱们这个示例,服务类是OrderSvr,可以这样来开启:

    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
internal class OrderSvr : IOrder
……

2、如果你不想用代码来设置,可以用配置文件,这样可以方便修改。方法是定义一个service behavior,并添加 serviceDebug 元素。如下

    <behaviors>
<serviceBehaviors>
<behavior name="svbhv">
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>

给它个名字(name),这样方便service元素去引用。

<service name="TestApp.OrderSvr" behaviorConfiguration="svbhv">
……

修改后,再次运行示例,然后输入不正确的参数,就能收到自定义的异常信息了。

         

大伙是不是很是兴奋,终于看到自定义的异常信息了。

================================================

问题似乎已经解决,不过,IncludeExceptionDetailInFaults 选项一般只是在调试阶段开启,正式上线的服务不应该开启,主要为了避免被别有用心的人调戏。所以,开启 IncludeExceptionDetailInFaults 选项还不是最终方案。那还有啥法子呢?

不急,广告回来揭晓……

在服务代码中抛出异常时可以选用 FaultException 类,而且,把一个 FaultReason 对象传给异常。FaultReason 是干吗的?它可以用来封装我们的自定义错误信息,为什么要用它呢,因为它帅?不是,因为一个 FaultReason 实例可以包含一个或N个 FaultReasonText 对象。

FaultReasonText不仅能设置错误描述文本,而且可以与区域/语言关联。比如,你的WCF服务是一个跨生物领域的应用,不仅人可以用,鸟儿、花儿、草儿都可以用,这样你需要返回多种语言版本的错误论处,比如中文的,韩文的,英文的,鸟语的,火星文的,等等。这样,每个 FaultReasonText 就可以封装一个版本的信息。

比如这样:

                FaultReasonText txt1 = new FaultReasonText("我喜欢你", "zh-CN");
FaultReasonText txt2 = new FaultReasonText("I like you", "en-US");
FaultReason reason = new FaultReason(new FaultReasonText[] { txt1, txt2 });

随后,你用这个 FaultReason 对象来 new 一个FaultException实例。

      throw new FaultException(reason);

现在,咱们对服务代码进行修改。

        public bool NewOrder(DateTime date, decimal price, long q)
{
// 验证
if (date < DateTime.Now)
{
FaultReasonText text = new FaultReasonText("至少要在今天下单");
FaultReason reason = new FaultReason(text);
throw new FaultException(reason);
}
if (price < || q < )
{
FaultReason reason = new FaultReason("单价或数量不能小于0");
throw new FaultException(reason);
}
// 模拟下单
if ((q * price) == 0.00M)
{
return false;
}
return true;
}

这里我们只要中文版本就可以了,所以不用弄那么多 FaultReasonText。

在客户端,捕捉到FaultException异常后,要从它的Reason属性中获得 FaultReason 对象,再调用 GetMatchingTranslation 方法来获得错误文本。如果调用的是无参数版本,就按当前语言来获取,你的系统是中文版的,就默认获取中文版错误信息。如果你的系统是鸟文版的,就默认获取鸟文版的错误信息。

            catch(FaultException ftex)
{
MessageBox.Show(ftex.Reason.GetMatchingTranslation().Text);
}

这样修改后,就算 includeExceptionDetailInFaults 选项的值为 false,也能获取到服务器上抛出的错误信息。

顺便提一句,如果WCF示例不能运行,请以管理员身份运行 VS。

好,今天就聊到这里,本文示例下载地址:点这里下载

【WCF】错误处理(一):FaultException 与 FaultReason 的搭配的更多相关文章

  1. WCF初探-20:WCF错误协定

    WCF错误协定概述 在所有托管应用程序中,处理错误由 Exception 对象表示. 在基于 SOAP 的应用程序(如 WCF 应用程序)中,服务方法使用 SOAP 错误消息来传递处理错误信息. SO ...

  2. WCF错误:由于目标计算机积极拒绝,无法连接;127.0.0.1:3456

    问题描述 最近Windows打完补丁,原来部署在本机的WCF无法连接:出现如下WCF错误:由于目标计算机积极拒绝,无法连接:127.0.0.1:3456 解决方案 检查一下本机的服务:NetTcpAc ...

  3. WCF错误处理

    介绍 WCF(Windows Communication Foundation) -异常处理:一般Exception的处理,FaultException和FaultException<T> ...

  4. WCF错误:由于目标计算机积极拒绝,无法连接

    今天学习WCF时用C#重写测试例子时,发生错误:由于目标计算机积极拒绝,无法连接.找了N久,网上也没有找到实际的解决方法.查看netstat -an发现当自承载宿主运行时,没有侦听配置的端口.开始总以 ...

  5. WCF错误:413 Request Entity Too Large

    在我们用WCF传输数据的时候,如果启用默认配置,传输的数据量过大,经常会出这个错误. WCF包含服务端与客户端,所以这个错误可能出现在服务端返回数据给客户端,或客户端传数据给服务端时. 1. 服务端返 ...

  6. WCF错误:413 Request Entity Too Large 的一个解决方法

    在我们用WCF传输数据的时候,如果启用默认配置,传输的数据量过大,经常会出这个WCF:413 Request Entity Too Large的错误. WCF包含服务端与客户端,所以这个错误可能出现在 ...

  7. WCF错误"The maximum message size quota for incoming messages (65536) has been exceeded."

    错误原因有三:超过最大接受的传输值 1.webconfig或者 app.config 文件中的binding 节点进行 配置 maxBufferSize="2147483647" ...

  8. 调用WCF错误-There was no endpoint listening

    问题描述: 今天在调用WCF服务时候出现了下面的错误. 原因: 调用服务的客户端ip设置成了固定ip.(至于固定ip为什么会导致这个错误,没能去研究) 解决方法: 将客户端ip设置成自动获取.

  9. wcf 错误提示

    wcf 不弹出错误提示,只显示“服务器处理请求时遇到错误.有关构造有效服务请求的内容,请参阅服务帮助页”,添加以下节点可以弹出错误提示. <serviceDebug includeExcepti ...

随机推荐

  1. Reactive 网络状态 Activity indicator view

    转动属性:Animating RAC(self.searchActivity, hidden) = [self.m_viewModel.m_searchCommand.executing not];

  2. jQuery对象插件封装步骤

    jQuery是js的一个非常优秀的库,它大大简化了js的很多操作,并且解决了js的大部分兼容性问题.甚至很多css兼容性问题,用jQuery写都能解决. 这里是对象插件的封装.当然,封装插件很多,这里 ...

  3. permutation test

  4. 在DFS和BFS中一般情况可以不用vis[][]数组标记

    开始学dfs 与bfs 时一直喜欢用vis[][]来标记有没有访问过, 现在我觉得没有必要用vis[][]标记了 看代码 用'#'表示墙,'.'表示道路 if(所有情况都满足){ map[i][j]= ...

  5. 第二部分 条件控制执行语句、循环语句、switch语句、跳转语句和其它语句

    条件控制执行语句: if语句 if....else....语句 循环语句: while语句 do....while语句 for语句 switch语句: 跳转语句: break; continue; r ...

  6. RocketMQ源码 — 三、 Consumer 接收消息过程

    Consumer pull message 订阅 在Consumer启动之前先将自己放到一个本地的集合中,再以后获取消费者的时候会用到,同时会将自己订阅的信息告诉broker 接收消息 consume ...

  7. 蓝桥杯java试题《洗牌》

    问题描述 小弱T在闲暇的时候会和室友打扑克,输的人就要负责洗牌.虽然小弱T不怎么会洗牌,但是他却总是输. 渐渐地小弱T发现了一个规律:只要自己洗牌,自己就一定会输.所以小弱T认为自己洗牌不够均匀,就独 ...

  8. removeEventListener('2016');

    2016----最后一天工作日要快结束了,趁剩下的一点时间写篇博客玩玩,想到啥就写啥.总结下来就一句---累并快乐着... 先祝大家新年快乐!万事如意发大财. 一年跳了三家公司,上半年在家小公司干着整 ...

  9. Linux下使用javac编译

    Linux下使用javac编译Hadoop程序 首先要配置好Hadoop, 给出两个教程 Hadoop安装教程单机/伪分布式配置Hadoop2.6.0/Ubuntu14.04 Hadoop集群安装配置 ...

  10. 使用PHP的strstr()函数来统计一段字符串中元音字母的个数(区分大小写)

    <?php/**练习:统计一段字符串中所有元音字母的个数(区分大小写)*/$str='This is a test file.'; //原始字符串echo $str.'<br>'; ...