2011-12-20 155 views
5

我對如何在我的SOAP請求中添加附件感到疏離。我們必須消費一個以java編寫的第三方Web服務,這是我遇到過的最令人費解的事情。我們使用的任何其他Web服務(即所需的附件)都具有用於添加附件的方法或屬性。簡單。但是,這個沒有提供這樣的方法。將附件添加到SOAP請求

我們已經收到了SOAP消息的一個版本,這與我們想要的XML完全相同,但它是我們無法添加的文件的MIME部分。

例子:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"> 
<soap:Header> 
<payloadManifest xmlns="http://<examplePayload>"> 
<manifest contentID="Content0" namespaceURI="http://<exampleManifest>" element="ProcessRepairOrder" version="2.01" /> 
</payloadManifest> 
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> 
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> 
<wsu:Created>2011-12-19T15:25:13Z</wsu:Created> 
<wsu:Expires>2011-12-19T15:30:00Z</wsu:Expires> 
</wsu:Timestamp> 
<wsse:UsernameToken><wsse:Username>username</wsse:Username><wsse:Password>password</wsse:Password></wsse:UsernameToken></wsse:Security></soap:Header><soap:Body><ProcessMessage xmlns="<examplePayload"><payload><content id="Content0"> 

<s:ProcessRepairOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://example.xsd" xmlns:s="http://<exampleManifest>" xmlns:gwm="http://example"> 
    <s:ApplicationArea> 
     <s:Sender> 
      <s:Component>Test</s:Component> 
      <s:Task>ProcessAttachment</s:Task> 
      <s:CreatorNameCode>Test</s:CreatorNameCode> 
      <s:SenderNameCode>XX</s:SenderNameCode> 
      <s:DealerNumber>111111</s:DealerNumber> 
      <s:DealerCountry>GB</s:DealerCountry> 
     </s:Sender> 
     <s:CreationDateTime>2010-03-26T13:37:05Z</s:CreationDateTime> 
     <s:Destination> 
      <s:DestinationNameCode>GM</s:DestinationNameCode> 
      <s:DestinationURI/> 
      <s:DestinationSoftwareCode>GWM</s:DestinationSoftwareCode> 
     </s:Destination> 
    </s:ApplicationArea> 
    <s:DataArea xsi:type="gwm:DataAreaExtended"> 
     <s:Process/> 
     <s:RepairOrder> 
      <s:Header xsi:type="gwm:RepairOrderHeaderExtended"> 
       <s:DocumentId/> 
      </s:Header> 
      <s:Job xsi:type="gwm:JobExtended"> 
       <s:JobNumber/> 
       <s:OperationId>Test</s:OperationId> 
       <s:OperationName/> 
       <s:CodesAndComments/> 
       <s:Diagnostics/> 
       <s:WarrantyClaim xsi:type="gwm:WarrantyClaimExtended"> 
        <s:OEMClaimNumber>00112233445566778899</s:OEMClaimNumber> 
        <gwm:Attachment> 
         <gwm:File><xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:test.gif"/></gwm:File> 
         <gwm:Filename>test.gif</gwm:Filename> 
        </gwm:Attachment> 
       </s:WarrantyClaim> 
       <s:LaborActualHours>0.0</s:LaborActualHours> 
       <s:Technician/> 
      </s:Job> 
     </s:RepairOrder> 
    </s:DataArea> 
</s:ProcessRepairOrder> 
</content></payload></ProcessMessage></soap:Body></soap:Envelope> 

這是我們可以生成和發送關閉XML的一部分,但它是不正確的,我們需要一個MIME部分有這樣的:

XML之前:

--MIMEBoundary 
Content-Type: application/xop+xml; charset=utf-8; type="text/xml" 
Content-Transfer-Encoding: binary 
Content-ID: <rootpart> 

XML後

--MIMEBoundary 
Content-Type: image/gif; name=test.gif 
Content-Transfer-Encoding: binary 
Content-ID: <test.gif> 
[email protected]� 

--MIMEBoundary-- 

我已經在互聯網上尋找答案,但已經空白。似乎沒有太多關於使用WSE的文檔。我必須強調,WSE是服務器端的一項要求,我無法改變技術來解決這個問題。

有沒有辦法可以添加這些MIME部分?

編輯:我必須補充說我可以通過附件發送通過SoapUI發送的工作XML文檔,但似乎無法在我們的代碼中找到方法。

我已經添加了一個賞金來試圖解決這個問題。如果有人有任何其他想法,請讓我知道。

再次編輯:我知道這已經過去了一個星期,因爲我能夠在這裏查看回復,但是當一些人提供了一個好主意的時候,我仍然畫空白。圍繞XopDocument及其方法的可怕文檔是一個很大的問題,如果任何人有任何使用SaveToXopPackage的例子,他們能否請他們提供,因爲這是開始磨合!

+2

WSE已過時,無論如何都不應使用。 –

+1

約翰,如何提出一個解決方案,而不是繞着假裝你知道一切。在這種情況下,對我而言,WSE是必需的。沒有它,我們的請求將被拒絕。我無法告訴第三方供應商他們應該如何編碼,我可以嘗試,但他們一直是世界上最沒有幫助的公司。因此,如果您正在創建自己的軟件,則不應使用WSE,但在此情況下這是一項要求。 – anothershrubery

+0

顯然這是一個商業決定,您的公司是否應該繼續與需要使用過時軟件的第三方開展業務,而不是與其他競爭對手不同。 –

回答

6

我面臨同樣的問題,我發現最終的解決方案是通過HttpWebRequest。 甲示例代碼:

public string ProcessAttachment(string fileInput) 
    { 
     HttpWebRequest req = (HttpWebRequest)WebRequest.Create(Settings.Default.GWM_WS_WebReference_GWM); 
     req.Headers.Add("SOAPAction", "\"http://www.starstandards.org/webservices/2005/10/transport/operations/ProcessMessage/v1_01/ProcessAttachment\""); 
     req.Headers.Add("Accept-Encoding", "gzip,deflate"); 
     req.ContentType = "multipart/related; type=\"application/xop+xml\"; start=\"<[email protected]>\"; start-info=\"text/xml\"; boundary=\"----=_Part_14_1350106.1324254402199\""; 
     req.Method = "POST"; 
     req.UserAgent = "Jakarta Commons-HttpClient/3.1"; 
     req.Headers.Add("MIME-Version", "1.0"); 
     System.Net.ServicePointManager.Expect100Continue = false; 
     Stream memStream = new System.IO.MemoryStream(); 
     FileStream fileStream = new FileStream(fileInput, FileMode.Open, FileAccess.Read); 
     byte[] buffer = new byte[1024]; 
     int bytesRead = 0; 
     while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) 
     { 
      memStream.Write(buffer, 0, bytesRead); 
     } 
     fileStream.Close(); 
     Stream stm = req.GetRequestStream(); 
     memStream.Position = 0; 
     byte[] tempBuffer = new byte[memStream.Length]; 
     memStream.Read(tempBuffer, 0, tempBuffer.Length); 
     memStream.Close(); 
     stm.Write(tempBuffer, 0, tempBuffer.Length); 
     stm.Close(); 
     HttpWebResponse resp = null; 
     resp = (HttpWebResponse)req.GetResponse(); 
     stm = resp.GetResponseStream(); 
     StreamReader r = new StreamReader(stm); 
     return r.ReadToEnd();    
    } 

參數的FileInput是包含還包含文件的原始二進制數據的SOAP請求的文件的絕對路徑在末端具有MIME邊界

+3

-1沒有「使用」塊。 –

+0

我真的不明白這是如何工作的。如果您要將內聯的原始數據包含在文件中,那麼在我的情況下這是不可能的。它必須包含在XOP部分中,就像我給出的示例中所定義的那樣。如果我在你的代碼中遺漏了這個,請告訴我。 – anothershrubery

+0

該文件應該包含您在第一篇文章中描述的2個部分:用Mime Boundary分隔的XML部分和Ras數據部分。原始數據部分表示您的「GIF89a @」部分應該用下面的代碼替換爲簡單字符串:byte [] bt = File.ReadAllBytes(imageFile); string raw = System.Text.Encoding.Unicode.GetString(bt) ;更好地與我聯繫以解釋,因爲我認爲這超出了計算器範圍。 –

3

分離附我認爲你可能有幾種選擇:

1)使用MTOM。這似乎會自動將傳出消息包裝在MIME塊中。

2)微軟實際上通過XopDocument類來提供對生成和讀取Xml的支持,這是SoapEnvelope從中繼承的。

保存方法是SaveToXopPackage,讀取方法是LoadFromXopPackage

但是,我認爲這種方法可能需要您通過HttpWebRequest自己執行發送消息。 This blog有一個如何實現這個的例子。缺點是這需要大量額外的代碼和配置才能正常工作。

理想的解決方案是攔截執行包絡傳輸的代碼,但我一直無法找到正確的位置。

+0

這些只是一些當我試圖讓這個工作起作用時,我使用過的引用數量。我可以讓它發送一個MIME封閉的塊,但我不知道如何將附件的二進制數據添加到MIME部分。 – anothershrubery

+1

另外,可能沒有'SaveToXopPackage'的文檔,Google會返回28個結果,其中大部分都是這個確切的頁面! – anothershrubery

1

正如你所說,你通過SoapUI工作,我認爲你可以問SoapUI生成的XML發送,所以你知道它應該看起來,然後修改你的代碼來模仿它。

更新:在您的評論和更詳細地閱讀其他答案後:解決方案看起來只是直接發送字節,使用HttpWebRequest就像在ktsiolis的答案。詳細介紹:

  • 創建SOAP XML(你給的例子),在UTF8編碼這字節(1)
  • 創建初始mimeboundary一個字符串(在部分你的「XML之前」),編碼爲UTF8(2)中的字節
  • 爲第二個mimeboundary(「XML之後」部分)創建字節。因此創建包含「--MIMEBOUNDARY」等字符串,編碼爲UTF8字節,並且附加test.gif文件的所有字節。(3)
  • 附加所有字節順序(2),(1)和(3)並通過電線發送。

不應該這樣做嗎?

+0

認真嗎?我完全知道它應該是什麼樣子,我只是不知道如何獲得附加到MIME部分的二進制數據。 – anothershrubery

2

我90%的自信我正在和你們一樣完成同樣的項目。肥皂請求有點太熟悉了:-)

我們已經通過切換到WCF,並基本上手工編寫請求對象(創建與soap格式匹配的類,然後使用xmlelement屬性裝飾它,使它看起來像它們的soap請求,文件本身在Attachment類中聲明爲Byte(),並且也用xmlelement裝飾)。

以下是WCF合同和數據模型的一部分。實際的數據模型有一些額外的類(應用程序區,數據區,作業等),但這足以讓你瞭解它的結構。重要的一部分是文件作爲字節()。這裏是在Vb.net ...

Public Class WarrantyClaim 
    <XmlElement(Order:=0)> Public OEMClaimNumber As String = "" 
    <XmlElement(Order:=1, namespace:="http://www.gm.com/2006/GWM")> Public Attachment As New Attachment 
End Class 

Public Class Attachment 
    <XmlElement(Order:=0)> Public File As Byte() 
    <XmlElement(Order:=1)> Public Filename As String 
End Class 

<ServiceContract(XmlSerializerFormat()> _ 
Public Interface IService 
    <OperationContract(action:="http://www.starstandards.org/webservices/2005/10/transport/operations/ProcessMessage/v1_01/ProcessAttachment")> _ 
    Sub ProcessMessage(ByVal payload As WarrantyClaim) 
End Interface 

接下來你已經有了你的WCF客戶端,這幾乎和所有的WCF客戶端一樣。

Public Class GmgwClient 
    Inherits System.ServiceModel.ClientBase(Of IService) 
    Implements IService 

    Public Sub New() 
     MyBase.New() 
    End Sub 
    Public Sub New(ByVal configName As String) 
     MyBase.New(configName) 
    End Sub 
    Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress) 
     MyBase.New(binding, remoteAddress) 
    End Sub 

    Public Sub ProcessMessage(ByVal payload As Payload) Implements IService.ProcessMessage 
     MyBase.Channel.ProcessMessage(payload) 
    End Sub 
End Class 

最後你得到了app.config。這是神奇的,因爲我們告訴WCF使用Mtom發送消息。這將採用Byte()並將其分離爲一個單獨的MIME部分,用XOP:Include替換它。請注意,現在我只是通過本地主機發送它,所以我可以使用tcpTrace查看請求。你可以谷歌應用程序,但它會基本捕捉請求,所以我們可以看到它的外觀。我設置tcpTrace爲偵聽端口84

<system.serviceModel> 
    <bindings> 
    <wsHttpBinding> 
     <binding name="WsHttpMtomBinding" messageEncoding="Mtom"> 
     <security mode="None"> 
      <transport clientCredentialType="Basic" proxyCredentialType="None" realm="" /> 
     </security> 
     <reliableSession enabled="false" /> 
     </binding> 
    </wsHttpBinding> 
    </bindings> 
    <client> 
    <endpoint address="http://localhost:84/ProcessMessage" binding="wsHttpBinding" bindingConfiguration="WsHttpMtomBinding" contract="MyAppNameSpace.IService" name="preprod"/> 
    </client> 
</system.serviceModel> 

最後,這對WCF客戶端的實際調用發出請求。

Dim x As New WarrantyClaim 
x.OEmClaimNumber = "12345" 
x.Attachment = New Attachment 
x.Attachment.Filename = "sample.gif" 
x.Attachment.File = IO.File.ReadAllBytes("C:\sample.gif") 

Dim y As New GmgwClient("preprod") 
y.ProcessMessage(x) 

這裏是我們通過tcpTrace獲得的跟蹤。它具有正確的基本結構,並且它設法將二進制數據從xml中提取出來並放置在單獨的MIME部分中。

POST /ProcessMessage HTTP/1.1 
MIME-Version: 1.0 
Content-Type: multipart/related; type="application/xop+xml";start="<http://tempuri.org/0>";boundary="uuid:501aa27d-9dd1-4f8a-b56d-3fbf327e7be6+id=1";start-info="application/soap+xml" 
VsDebuggerCausalityData: uIDPoysDMCv023ZIjK0Cpp504ooAAAAA//jfaCaohkab2Zx/EU7gpLZDcUldWtlGr1j4ZnrfKl4ACQAA 
Host: localhost:84 
Content-Length: 55125 
Expect: 100-continue 
Accept-Encoding: gzip, deflate 
Connection: Keep-Alive 


--uuid:501aa27d-9dd1-4f8a-b56d-3fbf327e7be6+id=1 
Content-ID: <http://tempuri.org/0> 
Content-Transfer-Encoding: 8bit 
Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml" 

<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">http://www.starstandards.org/webservices/2005/10/transport/operations/ProcessMessage/v1_01/ProcessAttachment</a:Action> 
    <a:MessageID>urn:uuid:a85374e6-c8ca-4328-ad32-6e8b88a5ca59</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:84/ProcessMessage</a:To> 
    </s:Header> 
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <ProcessMessage xmlns="http://www.starstandards.org/webservices/2005/10/transport"> 
     <payload xsi:type="gwm:WarrantyClaimExtended"> 
     <OEMClaimNumber>12345</OEMClaimNumber> 
     <Attachment xmlns="http://www.gm.com/2006/GWM"> 
      <File> 
      <xop:Include href="cid:http%3A%2F%2Ftempuri.org%2F1%2F634618782531246992" xmlns:xop="http://www.w3.org/2004/08/xop/include"/> 
      </File> 
      <Filename>sample.gif</Filename> 
     </Attachment> 
     </payload> 
    </ProcessMessage> 
    </s:Body> 
</s:Envelope> 
--uuid:501aa27d-9dd1-4f8a-b56d-3fbf327e7be6+id=1 
Content-ID: <http://tempuri.org/1/634618782531246992> 
Content-Transfer-Encoding: binary 
Content-Type: application/octet-stream 

GIF89a<BinaryStuff> 

就像我之前提到的 - 我們仍然有一些問題。 Soap Header中缺少一些標籤...但我認爲我們可以將其解決。真正的問題是Content-ID不是我們的合作伙伴可以接受的格式 - 他們期望像< [email protected]>和.net格式化爲http://tempuri.org/1/634618782531246992。這導致其Web服務處理程序崩潰,因爲它不知道如何讀取soap消息中的轉義內容標識符。

+0

我的電子郵件地址在我的個人資料中可以看到。 – anothershrubery

+0

現在就注意到您的編輯。是的,它看起來像完全相同的項目!不幸的是你的解決方案在我們的案例中不起作用,因爲我們被限制使用WSE而不是WCF ......依賴於VS 2005.這是一個痛苦。但這是我正在尋找的結果,但需要在WSE中找出解決方案。 :(但是,有了你的問題,你能不能將Content-ID設置爲你想要的任何東西?這可以在SoapUI中工作,當你自己指定內容ID並且不依賴默認表示時?我的電子郵件地址,在我的個人檔案中,如果你想進一步交談 – anothershrubery

0

好吧,讓我接受來自<gwm:File>元素中文件的數據。這是不使用XOP,因此請求現在看起來像:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"> <soap:Header> <payloadManifest xmlns="http://<examplePayload>"> <manifest contentID="Content0" namespaceURI="http://<exampleManifest>" element="ProcessRepairOrder" version="2.01" /> </payloadManifest> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsu:Created>2011-12-19T15:25:13Z</wsu:Created> <wsu:Expires>2011-12-19T15:30:00Z</wsu:Expires> </wsu:Timestamp> <wsse:UsernameToken><wsse:Username>username</wsse:Username><wsse:Password>password</wsse:Password></wsse:UsernameToken></wsse:Security></soap:Header><soap:Body><ProcessMessage xmlns="<examplePayload"><payload><content id="Content0"> <s:ProcessRepairOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://example.xsd" xmlns:s="http://<exampleManifest>" xmlns:gwm="http://example"> 
    <s:ApplicationArea> 
     <s:Sender> 
      <s:Component>Test</s:Component> 
      <s:Task>ProcessAttachment</s:Task> 
      <s:CreatorNameCode>Test</s:CreatorNameCode> 
      <s:SenderNameCode>XX</s:SenderNameCode> 
      <s:DealerNumber>111111</s:DealerNumber> 
      <s:DealerCountry>GB</s:DealerCountry> 
     </s:Sender> 
     <s:CreationDateTime>2010-03-26T13:37:05Z</s:CreationDateTime> 
     <s:Destination> 
      <s:DestinationNameCode>GM</s:DestinationNameCode> 
      <s:DestinationURI/> 
      <s:DestinationSoftwareCode>GWM</s:DestinationSoftwareCode> 
     </s:Destination> 
    </s:ApplicationArea> 
    <s:DataArea xsi:type="gwm:DataAreaExtended"> 
     <s:Process/> 
     <s:RepairOrder> 
      <s:Header xsi:type="gwm:RepairOrderHeaderExtended"> 
       <s:DocumentId/> 
      </s:Header> 
      <s:Job xsi:type="gwm:JobExtended"> 
       <s:JobNumber/> 
       <s:OperationId>Test</s:OperationId> 
       <s:OperationName/> 
       <s:CodesAndComments/> 
       <s:Diagnostics/> 
       <s:WarrantyClaim xsi:type="gwm:WarrantyClaimExtended"> 
        <s:OEMClaimNumber>00112233445566778899</s:OEMClaimNumber> 
        <gwm:Attachment> 
         <gwm:File>[email protected]�</gwm:File> 
         <gwm:Filename>test.gif</gwm:Filename> 
        </gwm:Attachment> 
       </s:WarrantyClaim> 
       <s:LaborActualHours>0.0</s:LaborActualHours> 
       <s:Technician/> 
      </s:Job> 
     </s:RepairOrder> 
    </s:DataArea> </s:ProcessRepairOrder> </content></payload></ProcessMessage></soap:Body></soap:Envelope> 

當傳遞到了SoapUI這個完美的作品,但在代碼中它確實給一個迴應,但它拋出一個錯誤說Response is not well-formed XML.WSE1608: No XOP parts were located in the stream for the specified content-id: <rootpart*[email protected]>內部異常

我會對此開一個新的問題,因爲這在技術上是一個不同的問題。

其他問題可以在Soap response, not well formed XML, no XOP parts located, using WSE

0

我參與完全相同的項目中找到我在這個線程討論同樣的問題! 我正在使用vb 2005和WSE 3.0增強功能,即使現在它仍然很痛苦,我已經開始工作了。當直接在文件屬性中寫入文件的內容時,該附件將被合作伙伴接受。 就我而言,這適用於除PRA之外的幾乎所有交易。在這裏,響應是肯定的,並且將傳遞附件ID,但附件不會出現在事務中。

這裏是連接部分的一個例子:

   <gwm:Attachment> 
        <gwm:File>/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ...</gwm:File> 
        <gwm:Filename>intro2.jpg</gwm:Filename> 
       </gwm:Attachment> 

如果我設置RequireMtom的服務爲True,我會收到以下錯誤:

Das Präfix '' kann nicht von '' in 'http://www.starstandards.org/webservices/2005/10/transport' innerhalb desselben Startelementtags neu definiert werden.

其中一方面,它工作,另一方面,我不確定它是否會與XOP元素一起發送。

+0

我曾與網絡服務開發者討論過如何將數據直接放在''元素中,他們說這不符合他們的規範,他們需要一個''元素。請參閱http://stackoverflow.com/questions/8805095/soap-response-not-well-formed-xml-no-xop-parts-located-using-wse瞭解我們遇到的問題的進一步描述。想要在這裏進一步討論,請在我的電子郵件地址中查看我的個人資料 – anothershrubery

+0

當然...但是我找不到您的電子郵件地址 –

+0

如果您在bio下看不到它,它現在位於About Me部分我的公關OFILE。 – anothershrubery