• 【WCF】错误处理(一):FaultException 与 FaultReason 的搭配


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

    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 < 0 || q < 0)
                {
                    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 < 0 || q < 0)
                {
                    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。 

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

  • 相关阅读:
    基于SAR对Linux资源的监控shell脚本
    Python3+RobotFramewok 用户自定义库的开发(四)
    Python3+RobotFramewok 循环判断以及Evaluate用法(三)
    Python3+RobotFramewok 快速入门(二)
    Python3+RobotFramewok RIDE环境搭建(一)
    MySQL主从双向同步
    笔记:网络协议
    Jmeter组成结构及运行原理
    Selenium WebDriver的实现及工作原理
    Jenkins+maven环境部署
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/6509090.html
Copyright © 2020-2023  润新知