添加SOAP请求的附件

时间:2011-12-20 11:48:45

标签: .net soap attachment mtom wse

我对如何在SOAP请求中添加附件感到宽松。我们必须使用java内置的第三方Web服务,这是我遇到的最复杂的事情。我们使用的任何其他Web服务(需要附件)都有方法或属性来添加附件。简单。但是,这个没有提供这样的方法。

我们一起得到了一个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>
GIF89a@�

--MIMEBoundary--

我已经在互联网上搜索了答案但是空白了。关于使用WSE,似乎没有太多关于此的文档。我必须强调WSE是服务器端的要求,我无法改变技术来解决这个问题。

是否可以添加这些MIME部分?

编辑:我必须补充一点,我可以通过带有附件的SoapUI发送一个可用的XML文档,但似乎无法在我们的代码中找到方法。

我已经添加了一笔赏金来尝试解决这个问题。如果有人有任何其他想法,请告诉我。

再次编辑:我知道这已经过了一周,因为我能够在这里查看回复,但有些人给出了一个好主意,在哪里看,我仍然在画一个空白。关于XopDocument及其方法的可怕文档是一个很大的问题,如果有人有任何使用SaveToXopPackage的例子他们可以提供,因为这已经开始了!

6 个答案:

答案 0 :(得分:7)

我遇到了同样的问题,我找到的最终解决方案是通过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=\"<rootpart@soapui.org>\"; 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 Request的文件的绝对路径,该SOAP Request还包含要用MIME Boundary分隔的末尾附加文件的原始二进制数据

答案 1 :(得分:4)

我认为你可能有几个选择:

1)使用MTOM。这似乎会自动将传出邮件包装在MIME块中。

2)Microsoft实际上通过XopDocument类提供了使用mime生成和读取XOP的支持,这是SoapEnvelope继承的。

保存方法为SaveToXopPackage,读取方法为LoadFromXopPackage

但是,我认为这种方法可能要求您通过HttpWebRequest自行发送消息。 This blog有一个如何实现这一点的例子。缺点是这需要大量额外的代码和配置才能正常工作。

理想的解决方案是截取执行包络传输的代码,但我无法在管道中找到正确的位置。

答案 2 :(得分:2)

我90%有信心我正在和你们一样完成同样的项目。肥皂请求有点太熟悉了: - )

通过切换到WCF并基本上手动编写请求对象(创建与soap格式匹配的类,然后使用xmlelement属性来装饰它,使其看起来像他们的soap请求),我们已经获得了大部分方法。文件本身在Attachment类中声明为Byte(),并且还使用xmlelement进行修饰。

这是WCF合同和数据模型的一部分。实际的数据模型有一堆额外的类(应用程序区域,数据区域,作业等),但这足以让您了解它的结构。重要的部分是File as Byte()。这是在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。请注意,现在我只是通过localhost发送它,所以我可以使用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不是我们的合作伙伴可以接受的格式 - 他们期望类似于&lt; 1.a33c2d7e84634122705ebc71e53d95d4c2683d726ba54e14@apache.org>和.net将它们格式化为http://tempuri.org/1/634618782531246992。这导致他们的Web服务处理程序崩溃,因为它不知道如何读取soap消息中的转义内容id。

答案 3 :(得分:1)

正如你所说,你通过SoapUI工作,我认为你可以向SoapUI询问它发送的生成的XML,这样你就知道它应该是什么样的,然后修改你的代码来模仿它。

更新:在您发表评论并更详细地阅读其他答案之后:解决方案在我看来只是直接发送字节,使用HttpWebRequest,就像在ktsiolis的答案中一样。详细说明:

  • 创建SOAP XML(您给出的示例),将其编码为UTF8(1)
  • 中的字节
  • 使用初始mimeboundary(“Before XML”中的部分)创建一个字符串,编码为UTF8中的字节(2)
  • 为第二个mimeboundary创建字节(“XML之后”部分)。 因此,创建包含“--MIMEBOUNDARY”等的字符串,编码为UTF8字节,附加test.gif文件的所有字节(3)
  • 在顺序(2),(1)和(3)中附加所有字节并通过网络发送。

不应该这样做吗?

答案 4 :(得分: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>GIF89a@�</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*36875c60-630c-4e23-9e74-f9a9c7547fc7@example.jaxws.sun.com>

我将就此开启一个新问题,因为从技术上讲,这是一个不同的问题。

另一个问题可以在Soap response, not well formed XML, no XOP parts located, using WSE

找到

答案 5 :(得分:0)

我参与完全相同的项目,我遇到了与此主题中讨论的相同的问题! 我正在使用vb 2005和WSE 3.0增强功能,即使它现在也很麻烦。在文件属性中直接写入文件内容时,合作伙伴将接受附件。 就我而言,这适用于除PRA之外的几乎所有交易。在这里,响应是肯定的,并且将传递AttachmentID,但附件不会出现在交易中。

以下是“附件”部分的示例:

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

如果我将服务的RequireMtom设置为True,我将收到以下错误:

  

DasPräfix''kannnicht von''在'http://www.starstandards.org/webservices/2005/10/transport'innerhalb desselben Startelementtags neu definiert werden。

另一方面,一方面它起作用,我不确定它是否会与XOP元素一起发送。