[WCF 學習筆記] 4. 消息操作

WCF 的一切都是圍繞着 Message 進行,那麼 Message 究竟是什麼樣子?

[ServiceContract]
public interface ICalculate
{
  [OperationContract]
  double Add(double a, double b);
}

public class CalculateService : ICalculate
{
  public double Add(double a, double b)
  {
    Message msg = OperationContext.Current.RequestContext.RequestMessage;
    Console.WriteLine(msg);

    return a + b;
  }
}

public class WcfTest
{
  public static void Test()
  {
    AppDomain.CreateDomain("Server").DoCallBack(delegate
    {
      ServiceHost host = new ServiceHost(typeof(CalculateService));
      host.AddServiceEndpoint(typeof(ICalculate), new BasicHttpBinding(),
        "http://localhost:8080/calc");

      host.Open();
    });

    ChannelFactory<ICalculate> factory = new ChannelFactory<ICalculate>(new BasicHttpBinding(),
      "http://localhost:8080/calc");
    ICalculate o = factory.CreateChannel();
    Console.WriteLine(o.Add(1, 2));
  }
}


輸出

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <To s:mustUnderstand="1" xmlns="http://...">http://localhost:8080/calc</To>
    <Action s:mustUnderstand="1" xmlns="http://...">http://tempuri.org/ICalculate/Add</Action>
  </s:Header>
  <s:Body>
    <Add xmlns="http://tempuri.org/">
      <a>1</a>
      <b>2</b>
    </Add>
  </s:Body>
</s:Envelope>


事實上我們可以直接基於 Message Layer 進行編程,利用 OperationContract.Action 捕獲特定 Action 的消息。

[ServiceContract]
public interface ICalculate
{
  [OperationContract(Action = "Add", ReplyAction="Add")]
  Message ProcessMessage(Message m);
}

public class CalculateService : ICalculate
{
  public Message ProcessMessage(Message m)
  {
    Data d = m.GetBody<Data>();
    Console.WriteLine(d.I);
    
    return Message.CreateMessage(MessageVersion.Soap11, "Add", new Data(9999));
  }
}

[DataContract]
public class Data
{
  [DataMember]
  public int I;

  public Data(int i)
  {
    this.I = i;
  }
}

public class WcfTest
{
  public static void Test()
  {
    AppDomain.CreateDomain("Server").DoCallBack(delegate
    {
      ServiceHost host = new ServiceHost(typeof(CalculateService));
      host.AddServiceEndpoint(typeof(ICalculate), new BasicHttpBinding(),
        "http://localhost:8080/calc");

      host.Open();
    });

    ChannelFactory<IRequestChannel> factory = new ChannelFactory<IRequestChannel>(
      new BasicHttpBinding(), "http://localhost:8080/calc");
    IRequestChannel channel = factory.CreateChannel();
    channel.Open();

    Message request = Message.CreateMessage(MessageVersion.Soap11, "Add", new Data(1234));
    Message reply = channel.Request(request);
    Console.WriteLine("-------------------");
    Console.WriteLine(reply);

    channel.Close();
    factory.Close();
  }
}    


輸出:

1234
-------------------
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
  <s:Body>
    <Data xmlns="http://..." xmlns:i="http://...">
      <I>9999</I>
    </Data>
  </s:Body>
</s:Envelope>


正如上面所看到的,所有的調用都被轉換成消息後發送。這也符合 SOA 的規範,完全隔離,清晰的邊界。(調用 "m.GetBody<Data>()" 後,會導致 Message.State 變更,再次訪問會出錯,有關詳細信息請參考 MSDN 文檔。)

我們還可以使用 MessageContractAttribute / MessageHeaderAttribute 來控制消息格式,這比 DataContractAttribute 要更加靈活。我們可以設置消息標頭、消息體,包括是否對其中某些進行簽名和加密處理。

[ServiceContract]
public interface ICalculate
{
  [OperationContract]
  void Add(Data d);
}

public class CalculateService : ICalculate
{
  public void Add(Data d)
  {
    Console.WriteLine(OperationContext.Current.RequestContext.RequestMessage);
    Console.WriteLine("----------------");
    Console.WriteLine("{0}/{1}", d.a, d.b);
  }
}

[MessageContract]
public class Data
{
  [MessageHeader]
  public double a = 1;

  [MessageBodyMember]
  public double b = 2;
}

public class WcfTest
{
  public static void Test()
  {
    AppDomain.CreateDomain("Server").DoCallBack(delegate
    {
      ServiceHost host = new ServiceHost(typeof(CalculateService));
      host.AddServiceEndpoint(typeof(ICalculate), new BasicHttpBinding(),
        "http://localhost:8080/calc");
      
      host.Open();
    });

    ChannelFactory<ICalculate> factory = new ChannelFactory<ICalculate>(new BasicHttpBinding(),
      "http://localhost:8080/calc");
    ICalculate o = factory.CreateChannel();

    Data d = new Data();
    d.a = 1234;
    d.b = 5678;
    o.Add(d);
  }
}


輸出:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:a xmlns:h="http://tempuri.org/">1</h:a>
    <To s:mustUnderstand="1" xmlns="http://s...">http://localhost:8080/calc</To>
    <Action s:mustUnderstand="1" xmlns="http://...">http://tempuri.org/ICalculate/Add</Action>
  </s:Header>
  <s:Body>
    <Data xmlns="http://tempuri.org/">
      <b>2</b>
    </Data>
  </s:Body>
</s:Envelope>
----------------
1234/5678


有關 Message 更詳細的信息,可以參考 MSDN (Microsoft Windows SDK) 文檔。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章