向SOAP请求添加附件

问题描述 投票:5回答:6

我对如何在SOAP请求中添加附件一无所知。我们必须使用基于Java的第三方Web服务,这是我遇到过的最复杂的事情。我们使用的任何其他Web服务(需要附件)都具有添加附件的方法或属性。简单。但是,此方法没有提供这种方法。

我们已经获得了与我们想要的XML完全相同的SOAP消息版本,但是它是我们无法添加的文件的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的示例,请他们提供,因为这开始令人讨厌了!

.net soap attachment mtom wse
6个回答
8
投票

我也遇到了同样的问题,最终发现的解决方案是通过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边界分隔开


4
投票

我认为您可能有两种选择:

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

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

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

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

理想的解决方案是拦截执行信封传输的代码,但我无法在管道中为此找到正确的位置。


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替换它。请注意,目前我只是通过本地主机发送它,因此我可以使用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服务处理程序崩溃,因为它不知道如何读取肥皂消息中的转义content-id。


1
投票

正如您说的那样,它是通过SoapUI运行的,我想您可以向SoapUI询问发送的生成的XML,这样您就可以知道它的外观,然后修改代码以模仿它。

更新:在您发表评论并更详细地阅读了其他答案之后:该解决方案在我看来只是直接使用ktsiolis答案中的HttpWebRequest发送字节。详细信息:

  • 创建您的SOAP XML(您提供的示例),将其编码为UTF8(1)中的字节
  • 创建一个带有初始微边界的字符串(“ Before XML”中的部分,以UTF8(2)编码为字节)>
  • 创建第二个微边界的字节(“ XML之后”的部分)。因此,创建包含“ --MIMEBOUNDARY”等的字符串,编码为UTF8字节,然后附加test.gif文件的所有字节
  • (3)
  • 以(2),(1)和(3)的顺序附加所有字节并通过网络发送。
  • 这不行吗?


0
投票

确定,所以我接受了<gwm:File>元素中文件中的数据。这是不使用XOP的,因此请求现在看起来像:


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.