8

我对如何在我的 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请提供,因为这开始令人讨厌!

4

6 回答 6

9

我遇到了同样的问题,我找到的最终解决方案是通过 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 请求的文件的绝对路径,其中还包含要附加在末尾的文件的原始二进制数据,并用 MIME 边界分隔

于 2011-12-20T21:18:36.600 回答
5

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

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

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

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

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

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

于 2011-12-31T04:38:40.760 回答
2

我有 90% 的信心我正在和你们做完全相同的项目。那个肥皂请求有点太熟悉了:-)

我们已经通过切换到 WCF 并基本上手动编码请求对象(创建与肥皂格式匹配的类,然后使用 xmlelement 属性对其进行装饰,使其看起来像他们的肥皂请求。文件本身在 Attachment 类上声明为 Byte() 并且还使用 xmlelement 进行装饰)。

下面是 WCF 契约和部分数据模型的样子。实际的数据模型有一堆额外的类(Application Area、Data Area、Job 等),但这让您对它的结构有足够的了解。重要的部分是 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 不是我们的合作伙伴可以接受的格式 - 他们期望像 <1.a33c2d7e84634122705ebc71e53d95d4c2683d726ba54e14@apache.org> 和 .net 将它们格式化为http://tempuri.org/1/634618782531246992 . 这导致他们的 Web 服务处理程序崩溃,因为它不知道如何读取肥皂消息中的转义内容 ID。

于 2012-01-07T00:23:54.987 回答
1

正如您所说,您通过 SoapUI 使其工作,我认为您可以向 SoapUI 询问它发送的生成的 XML,以便您知道它的外观,然后修改您的代码以模仿它。

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

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

这不应该解决问题吗?

于 2012-01-04T07:57:30.093 回答
0

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

以下是附件部分的示例:

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

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

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

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

于 2012-01-17T12:46:35.423 回答
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@�&lt;/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 响应中找到,不是格式良好的 XML,没有找到 XOP 部分,使用 WSE

于 2012-01-10T13:01:17.603 回答