XCF之原形

上篇回顾

    上篇提出了个思考,WCF的契约是否可以也基于配置,而不是必须要生成类型,并且从WCF的原理出发点阐述了其可能性,最后提出了XCF的概念。

    经过2周的努力,终于实现了一个原形,这一篇里面就讲一下怎么做一个XCF的原形。

实现基础

    首先,WCF客户端本身就支持发送任何消息。听起来很不可思议是吧?这就是大家的思维被WCF框架的思维定势所限制,反而把这些基础或者说原始的用法而忽略了。

    来看看一个关键接口:IRequestChannel

    当然,WCF的关键接口不止这一个,但是对付那些通常的基于tcp/http的WCF服务而言,这个接口就足够了。使用这个接口可以向tcp/http的wcf服务发送任何消息。当然,这里有个问题,任意消息必须要符合服务的协议,否则,服务端将返回错误。

准备服务端

    为了方便试验,先简单的准备一个服务端契约:

[ServiceContract(Namespace = "urn:test")]
public interface IEchoService
{
    [OperationContract(Action = "Echo")]
    Person Echo(Person p);
}
[DataContract(Namespace = "urn:test")]
public class Person
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public int Age { get; set; }
}

    以及一个简单的实现:

public class EchoService : IEchoService
{
    public Person Echo(Person p)
    {
        Console.WriteLine("Name={0},Age={1}", p.Name, p.Age);
        return p;
    }
}

    以及配置(加上wcf的日志):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="WSHttpBinding_IEchoService">
          <security mode="None" />
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="WcfHost.EchoServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="WcfHost.EchoServiceBehavior"
          name="WcfHost.EchoService">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IEchoService" contract="WcfHost.IEchoService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:12345/EchoService/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <diagnostics>
      <messageLogging
           logEntireMessage="true"
           logMalformedMessages="true"
           logMessagesAtServiceLevel="true"
           logMessagesAtTransportLevel="true"
           maxMessagesToLog="3000"
           maxSizeOfMessageToLog="2000"/>
    </diagnostics>
  </system.serviceModel>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="messages"
          type="System.Diagnostics.XmlWriterTraceListener"
          initializeData="messages.log" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
</configuration>

    在这里需要注意的是,要去除安全的部分,因为有安全的wcf会修改消息体,使用编码过的消息体,为原形带来不小的麻烦。

标准客户端

    作为对比,先准备一个标准的客户端,过程当然是按照标准的添加服务引用。

    然后,准备上一个标准的调用:

Svc.EchoServiceClient client = new WcfNormalClient.Svc.EchoServiceClient();
var resp = client.Echo(new WcfNormalClient.Svc.Person
{
    Name = "abc",
    Age = 12,
});
client.Close();
Console.WriteLine("Name={0},Age={1}", resp.Name, resp.Age);

    准备好客户端和服务端后,来一次模拟调用,再查看wcf日志,可以找到:

          <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
            <s:Header>
              <a:Action s:mustUnderstand="1">Echo</a:Action>
              <a:MessageID>urn:uuid:fb825687-ad4c-4eab-8b65-0f7c78b9f845</a:MessageID>
              <a:ReplyTo>
                <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
              </a:ReplyTo>
              <a:To s:mustUnderstand="1">http://localhost:12345/EchoService/</a:To>
            </s:Header>
            <s:Body>
              <Echo xmlns="urn:test">
                <p xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
                  <Age>12</Age>
                  <Name>abc</Name>
                </p>
              </Echo>
            </s:Body>
          </s:Envelope>

    这就是整个请求消息的消息体,也就是说,要实现Xcf的话,必须要完美的模拟这一请求。

Xcf类库准备

    略:文件下载

Xcf的使用

    首先,将类库解压并放在项目中,为了请求之前准备好的wcf服务,需要准备这样一个test.xml:

<?xml version="1.0" encoding="utf-8" ?>
<Echo xmlns="urn:test">
  <p>
    <Age>23</Age>
    <Name>def</Name>
  </p>
</Echo>

    这一段的内容与之前抓下来的那个消息中的Body部分一致。

    准备工作做好了,来show一下如何让这个原形调用到服务:

using (var f = new XcfChannelFactory(new WSHttpBinding()))
using (var c = f.Create(new Uri("http://localhost:12345/EchoService/")))
using (var reader = XmlReader.Create("Test.xml"))
using (var resp = c.Request("Echo", reader))
using (var respBody = resp.GetBody())
{
    Console.WriteLine(respBody.ReadInnerXml());
}

    其中,f是工程,c则是通道,reader用于获得一个请求的body,而请求由action和body两部分组成,resp用于保存返回消息,其中的respBody代表着resp消息中的主体。

    运行一下,可以在客户端显示出返回的消息,服务端也显示了请求的Name和Age,并且可以在日志中找到相应的请求和响应消息体。

下集预告

    到目前为止,原形已经很好的工作了,但是,不能更改请求的Wcf调用,显然是不实用的。别急,如何让这个请求xml活起来,就是下一篇的议题了。

原文地址:https://www.cnblogs.com/vwxyzh/p/1799675.html