复习WCF(6)----- 消息协定

今天看到一句很有感觉的话:你能作茧自缚,就能破茧成蝶

 

消息样式的操作最多具有一个参数和一个返回值,其中参数和返回值的类型都是消息类型

也就是说,这两种类型可直接序列化为指定的soap消息结构

可以用MessageContractAttribute标记的任何类型或Message类型(Message类型下次说)

        [OperationContract]
        bool Validate(BankingTransaction bt); //错误 返回值是bool类型 他不是消息类型 所以他是无效的消息协定

        [OperationContract]
        void Reconcile(BankingTransaction bt, BankingTransaction bt2);//错误 参数只能最多为一个 所以这也是无效的消息协定

 

若要为某一个类型定义消息协定(即定义该类型和soap信封之间的映射)

请对该类型使用MessageContractAttribute属性修饰,然后对该类型中成为soap标头的成员使用MessageHeaderAttribute

并对要成为消息的soap正文部分的成员使用MessageBodyMemberAttribute属性休息

如图所示:

虽然中间某些成员为私有的(private),但应为应用了消息属性修饰,所以可以忽略这个访问级别,因为它都会被序列化(数据协定也是如此)

接下来使用代码示例消息协定 先看项目工程结构:

服务端代码 Iservice.cs:

   [ServiceContract]
    public interface ICalculator
    {
        [OperationContract]
        MyMessage Calculate(MyMessage request); //使用MyMessage这个消息协定  因为返回值和参数都是消息协定 并且只有一个参数 所以这个基于消息的服务协定成立
    }


    /// <summary>
    /// 消息协定  可以用来作为以消息方式传输到客户端的承载类型 并且wcf的基础结构可以去序列化它
    /// </summary>
    [MessageContract]
    public class MyMessage
    {
        private string operation;
        private double n1;
        private double n2;
        private double result;

        //Constructor
        public MyMessage() { }

        //Constructor 
        public MyMessage(double n1, double n2, string operation, double result)
        {
            this.n1 = n1;
            this.n2 = n2;
            this.operation = operation;
            this.result = result;
        }

        //Constructor
        public MyMessage(MyMessage message)
        {
            this.n1 = message.n1;
            this.n2 = message.n2;
            this.operation = message.operation;
            this.result = message.result;
        }

        [MessageHeader]
        public string Operation  //标头
        {
            get { return operation; }
            set { operation = value; }
        }

        [MessageBodyMember]
        public double N1 //正文
        {
            get { return n1; }
            set { n1 = value; }
        }

        [MessageBodyMember]
        public double N2  //正文
        {
            get { return n2; }
            set { n2 = value; }
        }

        [MessageBodyMember]
        public double Result  //正文
        {
            get { return result; }
            set { result = value; }
        }

        [MessageHeader(MustUnderstand = true)]
        public string str;

    }

service.cs:

    public class CalculatorService : ICalculator
    {
        public MyMessage Calculate(MyMessage request)
        {
            MyMessage response = new MyMessage(request);
            switch (request.Operation) 
            {
                case "+":
                    response.Result = request.N1 + request.N2;
                    break;
                case "-":
                    response.Result = request.N1 - request.N2;
                    break;
                case "*":
                    response.Result = request.N1 * request.N2;
                    break;
                case "/":
                    response.Result = request.N1 / request.N2;
                    break;
                default:
                    response.Result = 0.0D;
                    break;
            }
            return response;
        }
    }

app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <!-- 部署服务库项目时,必须将配置文件的内容添加到 
  主机的 app.config 文件中。System.Configuration 不支持库的配置文件。-->
  <system.serviceModel>
    <services>
      <service name="WcfServiceLibrary1.CalculatorService">
        <endpoint address="" binding="wsHttpBinding" contract="WcfServiceLibrary1.ICalculator">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/CalculatorService/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- 为避免泄漏元数据信息,
          请在部署前将以下值设置为 false 并删除上面的元数据终结点  -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- 要接收故障异常详细信息以进行调试,
          请将以下值设置为 true。在部署前设置为 false 
            以避免泄漏异常信息-->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

</configuration>

客户端代码 Program.cs:

    class Program
    {
        static void Main(string[] args)
        {
            CalculatorClient client = new CalculatorClient();

            MyMessage request = new MyMessage();
            request.N1 = 100;
            request.N2 = 15.99;
            request.Operation = "+";
            MyMessage response = ((ICalculator)client).Calculate(request);
            Console.WriteLine("Add({0},{1}) = {2}", request.N1, request.N2, response.Result);

            request = new MyMessage();
            request.N1 = 145D;
            request.N2 = 76.54D;
            request.Operation = "-";
            response = ((ICalculator)client).Calculate(request);
            Console.WriteLine("Subtract({0},{1}) = {2}", request.N1, request.N2, response.Result);

            request = new MyMessage();
            request.N1 = 9D;
            request.N2 = 81.25D;
            request.Operation = "*";
            response = ((ICalculator)client).Calculate(request);
            Console.WriteLine("Multiply({0},{1}) = {2}", request.N1, request.N2, response.Result);

            request = new MyMessage();
            request.N1 = 22D;
            request.N2 = 7D;
            request.Operation = "/";
            response = ((ICalculator)client).Calculate(request);
            Console.WriteLine("Divide({0},{1}) = {2}", request.N1, request.N2, response.Result);

            client.Close();

            Console.WriteLine();
            Console.WriteLine("Press <ENTER> to terminate client.");
            Console.ReadLine();
        }
    }

 

在消息协定内部使用数组 

1 使用MessageHeaderAttribute:

2 使用MessageHeaderArrayAttribute:

相信区别已经很明显了

 

在消息协定内部使用自定义类型:

和上面的代码差不多,就是加一个数据协定,然后在消息协定内创建一个此数据协定类型的成员

数据协定:

   [DataContract]
    public class ExtType
    {
        public ExtType() { }
        public ExtType(string _name1,string _name2)
        {
            name1 = _name1;
            name2 = _name2;
        }

        private string name1;
        private string name2;

        [DataMember]
        public string Name1
        {
            get { return name1; }
            set { name1 = value; }
        }

        [DataMember]
        public string Name2
        {
            get { return name2; }
            set { name2 = value; }
        }
    }

为上面的消息协定增加成员:

       [MessageBodyMember]
        public ExtType ExtType
        {
            get { return et; }
            set { et = value; }
        }

稍微修改下服务的实现:

    public class CalculatorService : ICalculator
    {
        public MyMessage Calculate(MyMessage request)
        {
            MyMessage response = new MyMessage(request);
            switch (request.Operation)
            {
                case "+":
                    response.Result = request.N1 + request.N2;
                    break;
                case "-":
                    response.Result = request.N1 - request.N2;
                    break;
                case "*":
                    response.Result = request.N1 * request.N2;
                    break;
                case "/":
                    response.Result = request.N1 / request.N2;
                    break;
                default:
                    response.Result = 0.0D;
                    break;
            }
        // ************
            if (!request.ExtType.Equals(null))
            response.ExtType = new ExtType(request.ExtType.Name1 + "11", request.ExtType.Name2 + "22");
// ************
return response; } }

意思就是 如果消息协定内的这个ExtType成员不为空,就将ExtType成员类的某些字段值追加一些字符串

很简单明了吧  ,若要在消息协定内使用自定义类型,实际上就是追加一个数据协定成员 (数据协定类使用自定义成员也是如此)

 

 

对消息部分进行签名和加密

通过在MessageHeaderAttribute和MessageBodyMemberAttribute属性上设置otectionLevel来完成

None(不加密和签名)

Sign(仅数字签名)

EncryptAndSign(加密并数字签名)

默认值为None

note:若要让这些安全功能起作用,必须正确配置绑定和行为。如果在没有正确配置的情况下使用这些安全功能,则会在验证时引发异常

有一点需要注意的是,对于消息头,会分别为每个消息头确定其保护级别,就是每个消息头的保护级别都是单独的、独立的

但正文有点不一样,如果有多个正文,只要有任何一个正文的保护级别比较高,整个正文都保护级别都会被最高的那个给覆盖

recordID标头未受保护,patienName为signed,SSN进行了加密和签名。

即使comments和diagnosis正文指定的保护级别低,因为有一个正文部分medicaHistory应用了EncryptAndSign,所以整个消息正文都会默认应用EncryptAndSign。

安全的部分以后在讨论  这里主要介绍消息协定的部分

 

 

控制标头和正文部分的名称与命名空间:

在属性上使用Name和Namespace设置  如图:

对应序列化soap的xml是:

整个的消息协定都会包含在那个<s:Body里面

如果我们不想用默认的这种包装方式,也可以自定义,自定义方式就是在[MessageContract]里,将IsWrapped设置为False,然后设置WrapperName和WrapperNamespace

这只要了解下就好了

 

 

性能问题:

每个消息头和正文相互独立的进行序列化。因此可以为每个标头和正文重新声明命名空间。为了提高性能,特别是对于消息在网络中的大小

可以将多个标头和正文合并成一个标头和正文

这个就有点浪费 序列化出来有很多节点

比较好的方式就是将正文部分全部合并成一个 通过放在数据协定里 能提高一些性能

 

MustUnderstand=true:

在标头设置此属性,就代表客户端也必须有这个成员,并且得对此成员赋值(必须的)

比如服务端某消息协定有[DataHeader(MustUnderstand=true)]public string str;

那么客户端的代理类必须得有这个属性,并且这个属性也得有MustUnderstand=true的设置,并且在使用中必须得对str这个属性赋值

posted @ 2013-02-23 22:20  韬韬韬你羞得无礼  Views(221)  Comments(0)    收藏  举报