2012-12-06 57 views
3

我收到了客戶關於其Web服務客戶端如何工作的規範。規範是從服務發送和接收的實際SOAP XML消息以及相應的XSD。客戶希望我實施符合客戶的網絡服務。客戶端使用axis2 ws-stack編寫,我想要做的是在WCF中創建一個Web服務,它將接受客戶端發出的請求,並返回符合他們期望的XML的響應。在這個問題中,我只會發布與請求相關聯的XML和XSD,因爲如果我可以得到這個工作,響應將以類似的方式進行。WCF MessageContract環繞和列表

我得到的XML如下:

POST /axis2/services/SampleService HTTP/1.1 
Content-Type: text/xml; charset=UTF-8 
SOAPAction: "sendCommand" 
User-Agent: Axis2 
Host: 127.0.0.1:7777 
Content-Length: 347 
<?xml version='1.0' encoding='UTF-8'?> 
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
    <soapenv:Body> 
     <SendCommandRequest xmlns="http://something.org/"> 
     <CMD> 
      <Station Address="ABC"> 
       <Platform Address="DEF"> 
        <Command>5</Command> 
       </Platform> 
      </Station> 
     </CMD> 
     </SendCommandRequest> 
    </soapenv:Body> 
</soapenv:Envelope> 

這是相應的XSD的樣子:

<xsd:complexType name="SendCommandRequestType"> 
    <xsd:sequence> 
     <xsd:element name="Station"> 
      <xsd:complexType> 
       <xsd:attribute name="Address" type="xsd:string" use="required" /> 
       <xsd:sequence> 
        <xsd:element minOccurs="0" maxOccurs="1" name="Platform"> 
         <xsd:complexType> 
          <xsd:attribute name="Address" type="xsd:string" use="required" /> 
          <xsd:sequence> 
           <xsd:element name="Command"> 
            <xsd:simpleType> 
             <xsd:restriction base="xsd:string"> 
              <xsd:enumeration value="-1"/> 
              <xsd:enumeration value="0"/> 
              <xsd:enumeration value="1"/> 
              <xsd:enumeration value="2"/> 
              <xsd:enumeration value="3"/> 
              <xsd:enumeration value="4"/> 
              <xsd:enumeration value="5"/> 
             </xsd:restriction> 
            </xsd:simpleType> 
           </xsd:element> 
          </xsd:sequence> 
         </xsd:complexType> 
        </xsd:element> 
       </xsd:sequence> 
      <xsd:complexType> 
     </xsd:element> 
    </xsd:sequence> 
</xsd:complexType> 

我已經開始寫類型的WCF/MessageContract格式,但我在列表等方面遇到困難,因爲它們是雙重包裝的。

我MessageContracts看起來是這樣的:

[MessageContract(WrapperName = "SendCommandRequest", WrapperNamespace = "http://something.org/")] 
public class SendCommandRequest 
{ 
    [MessageBodyMember(Name="CMD")] 
    public CMD cmd = new CMD(); 
} 

[MessageContract(IsWrapped=false)] 
public class CMD 
{ 
    [MessageBodyMember(Name="Station")] 
    public List<Station> stations = new List<Station>(); 
} 

[MessageContract(IsWrapped=false)] 
public class Station 
{ 

    [MessageBodyMember] 
    public List<Platform> platforms = new List<Platform>(); 
    [MessageBodyMember(Name="Address")] 
    public String Address; 
} 

[MessageContract(WrapperName = "Platform")] 
public class Platform 
{ 
    [MessageBodyMember(Name = "Address")] 
    public String Address; 
} 

當我使用了SoapUI來我從網絡服務的以下響應:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> 
    <s:Body> 
     <SendCommandRequest xmlns="http://ttraflinariawebservice.org/"> 
     <CMD xmlns="http://tempuri.org/" xmlns:a="http://schemas.datacontract.org/2004/07/GeldImport" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 
      <a:stations> 
       <a:Station> 
        <a:Address>test</a:Address> 
        <a:platforms> 
        <a:Platform> 
         <a:Address>b</a:Address> 
        </a:Platform> 
        </a:platforms> 
       </a:Station> 
      </a:stations> 
     </CMD> 
     </SendCommandRequest> 
    </s:Body> 
</s:Envelope> 

正如你可以看到它不適合客戶期望的XML格式。我怎樣才能讓MessageContract符合客戶期望的XML?我不知何故需要使List不像他們那樣進行雙重包裝,並且類的屬性似乎被添加到類名中。

如果您希望我提供更多的信息和代碼,我可以這樣做。不想在整個帖子中填寫可能與問題無關的內容。

編輯:

提供的XSD文件格式不正確。爲了解決這個問題,我從提供的XML文件重新生成了一個XSD。然後我使用WSCF.blue工具爲XSD生成數據合同代碼。

予改變,使得服務契約使用文檔格式litteral遵守的axis2 SOAP1.1

[XmlSerializerFormat(Use = OperationFormatUse.Literal, Style = OperationFormatStyle.Document, SupportFaults = true)] 
[ServiceContract] 
public interface MyService 

我也改變了操作合同具有System.ServiceModel.Channels.Message作爲輸入和輸出消息,並然後使用生成的類(我從XSD生成的)手動序列化和反序列化xml。

回答

4

您最初的嘗試是使用[MessageContract]類來定義數據消息的契約(模式)。消息協定只用作最頂層的類(定義進入消息頭的內容和進入正文的內容)。其他類需要使用您使用的任何序列化程序的屬性(因爲您有XML屬性,所以需要使用XmlSerializer)。下面的代碼顯示瞭如何獲得符合您提供的模式的對象模型。您可以使用諸如Fiddler之類的工具查看它發送的請求。

public class StackOverflow_13739729 
{ 
    [ServiceContract(Namespace = "http://something.org")] 
    public interface ITest 
    { 
     [XmlSerializerFormat, OperationContract(Name = "sendCommand")] 
     void SendCommand(SendCommandRequest req); 
    } 

    public class Service : ITest 
    { 
     public void SendCommand(SendCommandRequest req) 
     { 
      Console.WriteLine("In service"); 
     } 
    } 

    [MessageContract(WrapperName = "SendCommandRequest", WrapperNamespace = "http://something.org")] 
    public class SendCommandRequest 
    { 
     [MessageBodyMember] 
     public CMD CMD { get; set; } 
    } 

    [XmlType] 
    public class CMD 
    { 
     [XmlElement] 
     public Station Station { get; set; } 
    } 

    public class Station 
    { 
     [XmlAttribute] 
     public string Address { get; set; } 

     [XmlElement] 
     public Platform Platform { get; set; } 
    } 

    public class Platform 
    { 
     string[] validCommands = new[] { "-1", "0", "1", "2", "3", "4", "5" }; 
     string[] command; 

     [XmlAttribute] 
     public string Address { get; set; } 

     [XmlElement] 
     public string[] Command 
     { 
      get { return this.command; } 
      set 
      { 
       if (value != null) 
       { 
        if (!value.All(c => validCommands.Contains(c))) 
        { 
         throw new ArgumentException("Invalid command"); 
        } 
       } 

       this.command = value; 
      } 
     } 
    } 

    public static void Test() 
    { 
     string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; 
     ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress)); 
     host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), ""); 
     host.Open(); 
     Console.WriteLine("Host opened"); 

     ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress)); 
     ITest proxy = factory.CreateChannel(); 

     SendCommandRequest req = new SendCommandRequest 
     { 
      CMD = new CMD 
      { 
       Station = new Station 
       { 
        Address = "ABC", 
        Platform = new Platform 
        { 
         Address = "DEF", 
         Command = new string[] { "5" } 
        } 
       } 
      } 
     }; 

     proxy.SendCommand(req); 

     ((IClientChannel)proxy).Close(); 
     factory.Close(); 

     Console.Write("Press ENTER to close the host"); 
     Console.ReadLine(); 
     host.Close(); 
    } 
}