zoukankan      html  css  js  c++  java
  • 【WCF】错误协定声明

    在上一篇烂文中,老周给大伙伴们介绍了 IErrorHandler 接口的使用,今天,老周补充一个错误处理的知识点——错误协定。

    错误协定与IErrorHandler接口不同,大伙伴们应该记得,上回我们是把自己实现IErrorHandler接口的类型添加到ChannelDispatcher中的,也就是说,IErrorHandler处理的是通道层的错误,它可以捕捉到多个服务操作上发生的错误。而错误协定是面向协定层的,它是通过 FaultContractAttribute 来声明的,这个特性在上一篇文章中我们用过,它应用的目标是方法。故而这个特性是针对某个服务操作而定义的,主要指明在某个服务操作可能会引发的  FaultException<TDetail> 异常,其中的TDetail泛型参数的类型是在FaultContract特性中指定。

    在上一篇烂文中提到过,如果是像string、int这些基本类型,是可以直接用的,如果是自定义的类型,则应该将其声明为数据协定,然后在FaultContract特性中作声明。

    咱们来实战一下。首先,咱们定一个服务协定。

        [ServiceContract(Namespace = "zhou-demo", Name = "runner")]
        public interface IDemo
        {
            [OperationContract(Action = "getNum", ReplyAction = "getNumRes")]
            [FaultContract(typeof(ErrorData), Namespace = "zhou-demo/faults", Name = "fault-ct", Action = "fault-back")]
            int GetNumber();
        }

    这个协定没什么重要的事情干,无非就是返回一个整数,待会儿咱们让它返回随机整数。

    不过大家注意,我在方法上应用了FaultContract特性,它的构造函数是个Type,这个Type用来指定存放错误详细信息的类型,这个类型就是FaultException<TDetail>中的TDetail的类型。其他属性如Action,Namespace、Name这些,不用我多说了吧,都知道干吗用的,你也可以不设置的,它会自动生成一个默认值。

    ErrorData是我自己定义的一个数据协定,请看下面代码。

        [DataContract(Namespace = "zhou-demo/fault-data")]
        public class ErrorData
        {
            [DataMember]
            public int ErrorNumber { get; set; }
            [DataMember]
            public string ErrorSource { get; set; }
            [DataMember]
            public string ErrorMessage { get; set; }
            [DataMember]
            public string ErrorType { get; set; }
        }

    这个好懂吧,不必介绍了吧。

    当服务操作中发生异常时,可以用ErrorData类来封装自定义的错误信息,WCF会把它序列化,然后塞进SOAP消息中发回给客户端,客户端就可以catch到这些错误数据了。

    接下来,实现服务类。

        class DemoService : IDemo
        {
            public int GetNumber()
            {
                Random rand = new Random();
                int n = rand.Next(2000);
                if(n % 3 == 0)
                {
                    ErrorData erdata = new ErrorData
                    {
                        ErrorNumber = n,
                        ErrorSource="人品",
                        ErrorType="生物机能错误",
                        ErrorMessage ="运气指数不佳,引发不可修复错误。"
                    };
                    throw new FaultException<ErrorData>(erdata);
                }
                return n;
            }
        }

    这代码没什么技术含量,你如果看不懂,应该写一份30万字的检讨书。随机生成一个整数,如果这个整数可以被3整除,那就抛出异常,否则正常返回这个数值。

    服务完成,下面该轮到实例化ServiceHost了,有了Host我们才能调用服务。Host的处理很TMD简单,直接指定:基址 + 服务类型 = 完事。

                Uri baseAddress = new Uri("http://localhost:9973");
                ServiceHost host = new ServiceHost(typeof(DemoService), baseAddress);
                host.Open();
                Console.WriteLine("服务已启动。");
                 ……
                host.Close();

    还记得老周以前说过吧,如果仅仅指定基址,而不定义任何EndPoint的话,Host会自动根据协定个数和基址个数,自动生成默认的终结点。在这个高大上例子中,基址是http方案的,所以生成的终结点默认使用BasicHttpBinding,由于服务类只实现了一个服务协定接口,所以该Host中也就只有一个终结点了。

    好了,万事具备,只欠东风了,东风就是客户端调用代码。

                Console.WriteLine("请按 Esc 键退出,按任意键调用服务。");
                ConsoleKeyInfo keyinfo;
                while((keyinfo = Console.ReadKey(true)).Key != ConsoleKey.Escape)
                {
                    EndpointAddress epaddr = new EndpointAddress(baseAddress);
                    BasicHttpBinding binding = new BasicHttpBinding();
                    IDemo channel = ChannelFactory<IDemo>.CreateChannel(binding, epaddr);
                    try
                    {
                        int x = channel.GetNumber();
                        Console.WriteLine($"
    调用结果:{x}。");
                    }
                    catch(FaultException<ErrorData> ftex)
                    {
                        ErrorData detail = ftex.Detail;
                        string msg = $"
    错误数字:{detail.ErrorNumber}
    " +
                            $"错误源:{detail.ErrorSource}
    " +
                            $"错误类型:{detail.ErrorType}
    " +
                            $"错误详情:{detail.ErrorMessage}";
                        Console.WriteLine(msg);
                    }
                    catch(FaultException fex)
                    {
                        Console.WriteLine(fex.Message);
                    }
                    catch(Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    finally
                    {
                        IClientChannel client = (IClientChannel)channel;
                        client.Close();
                    }

    为了能够在控制台应用程序中多次调用服务(整数是随机生成,不能保证每次都抛异常),老周做了些处理,按任意键来调用服务,当然不能弄成死循环,为了使循环不死,我留了个出口——按Esc键。

    如果在服务器上果真引发了FaultException<TDetail>,那么,在客户端就可以捕捉到该异常。服务器引发异常后会数据对象序列化,传到客户端后,会进行反序列化。

    示例运行结果如下图所示。

    好了,知识点补完了,老周得准备吃lunch了,今天是国庆长假第二天,下午老周要去朋友H家里看现场水墨绘画,据说有不少妹子在那里。

    示例代码下载

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

    很久没讲故事了,这回就讲一讲老周为什么会进入IT界吧。

    其实原因也极其简单,也不是什么远大志向,老周之所以会进入IT领域,就是因为喜欢编程,纯粹就是爱编程,不等于爱做项目,老周很讨厌做项目。

    还是孔爷爷说得好,“知之者不如好之者,好之者不如乐之者”,对老周而言,除了卖保险之外,什么行业都可以进,要不是热爱编程,老周是不会选择走IT道路。而编程里面,犹爱.NET。

    所以,以后你要是想跟老周讨论编程问题,那就只讨论编程上的事,不要跟我讨论怎么找到好工作,找工作那是你的事,跟我没关系,也不要讨论.net有什么前景之类的话题,那是匹夫才讨论的问题。不过,最近些天,好像又有些不学无术的人,又在搞.NET与Java之争,那些东西在老周眼里,等同于看一场相声表演而已。

    反正老周只告诉你一句话:我就是热爱.NET。

    如果你喜欢.NET,欢迎你看看老周写的烂文章;如果你不喜欢.NET,那无所谓,你可以无视老周的博客。但是,老周不允许有人在博客评论里面说不文明的言论。心诚则与君交,心不诚则与君绝。

  • 相关阅读:
    汽车租赁系统
    两种设计模式(2)==>>"单例"
    两种设计模式(1)==>>“简单工厂”
    面向对象设计的七大原则
    springboot中的mybatis是如果使用pagehelper的
    vue
    vue
    vue
    idea快捷键
    idea部署tomcat项目时,在项目里打断点不能拦截
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/5927285.html
Copyright © 2011-2022 走看看