Добавление вложения в запрос SOAP - PullRequest
5 голосов
/ 20 декабря 2011

Я не знаю, как добавить вложение в моем SOAP-запросе. Мы должны использовать сторонний веб-сервис, построенный на Java, что является самой запутанной вещью, с которой я когда-либо сталкивался. Любые другие используемые нами веб-сервисы, для которых требуются вложения, имеют метод или свойство для добавления вложения. Просто. Однако этот метод не предоставляет такого метода.

Мы собрали вместе версию сообщения 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?

РЕДАКТИРОВАТЬ: Я должен добавить, что я могу получить рабочий XML-документ, отправленный через SoapUI с вложениями, но не могу найти пути в нашем коде.

Я добавил вознаграждение, чтобы попытаться найти решение этой проблемы. Если у кого-то есть другие идеи, пожалуйста, дайте мне знать.

РЕДАКТИРОВАТЬ еще раз: я знаю, что прошла неделя с тех пор, как я смог проверить ответы здесь, но в то время как некоторые дают хорошую идею, где искать, я все еще рисую пробел. Ужасная документация, окружающая XopDocument и ее методы, является большим камнем преткновения, если у кого-нибудь есть примеры использования SaveToXopPackage, могут они предоставить, потому что это начинает радовать!

Ответы [ 6 ]

8 голосов
/ 21 декабря 2011

Я столкнулся с той же проблемой, и окончательное решение, которое я нашел, было через 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

4 голосов
/ 31 декабря 2011

Я думаю, что у вас может быть несколько вариантов:

1) Использовать MTOM .Похоже, это автоматически оборачивает исходящее сообщение в блоки MIME.

2) Microsoft фактически предоставляет поддержку для генерации и чтения XOP с помощью mime через класс XopDocument, который наследует SoapEnvelope.

Метод сохранения - SaveToXopPackage , метод чтения - LoadFromXopPackage .

Однако я думаю, что для этого подхода может потребоваться выполнить отправкусообщения самостоятельно через HttpWebRequest. В этом блоге есть пример того, как это реализовать.Недостатком является то, что для правильной работы требуется много дополнительного кода и конфигурации.

Идеальным решением было бы перехватить код, который выполняет передачу огибающей, но я не смог найти правильное местоположениедля этого в трубопроводе.

2 голосов
/ 07 января 2012

Я уверен на 90%, что работаю над тем же проектом, что и вы, ребята.Этот запрос на мыло немного знаком: -)

Мы прошли большую часть пути, переключившись на WCF и фактически написав вручную код объекта запроса (создав классы, соответствующие формату мыла, а затем используяАтрибуты xmlelement, чтобы украсить его так, чтобы он выглядел как их запрос мыла. Сам файл объявлен как Byte () в классе Attachment, а также декорирован 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>

Как я уже упоминал ранее - у нас все еще есть некоторые проблемы.В заголовке мыла отсутствуют некоторые теги ... но я думаю, мы сможем это выяснить.Настоящая проблема в том, что Content-ID НЕ в формате, который может принять наш партнер - они ожидают что-то вроде <1.a33c2d7e84634122705ebc71e53d95d4c2683d726ba54e14@apache.org> и .net форматирует их как http://tempuri.org/1/634618782531246992. Это вызывает их вебСбой обработчика службы, поскольку он не знает, как прочитать экранированные идентификаторы содержимого внутри сообщения мыла.

1 голос
/ 04 января 2012

Поскольку вы говорите, что он работает через SoapUI, я думаю, вы можете просто спросить SoapUI о сгенерированном XML, который он отправил, чтобы вы знали, как он должен выглядеть, а затем изменить свой код, чтобы имитировать это.

ОБНОВЛЕНИЕ: после вашего комментария и более подробного прочтения других ответов: мне кажется, что решение просто отправляет байты напрямую, используя HttpWebRequest, как в ответе Кциолиса. Подробно:

  • Создайте свой SOAP XML (пример, который вы привели), закодируйте его в байты в UTF8 (1)
  • Создайте строку с начальной границей mimeboundary (часть в вашем "Before XML"), закодируйте в байты в UTF8 (2)
  • Создайте байты для второго мимеобъекта (часть в «после XML»). Поэтому создайте строку, содержащую «--MIMEBOUNDARY» и т. Д., Закодируйте в байты UTF8 и добавьте все байты вашего файла test.gif (3)
  • Добавьте все байты в порядке (2), (1) и (3) и отправьте их по проводам.

Разве это не должно сработать?

0 голосов
/ 17 января 2012

Я участвую в одном и том же проекте, и у меня есть те же проблемы, которые обсуждались в этой теме!Я использую усовершенствования vb 2005 и WSE 3.0, и у меня все получилось, хотя сейчас это больно.При записи содержимого файла непосредственно в свойство файла, вложение будет принято партнером.В моем случае это работает почти для всех транзакций, кроме PRA.Здесь, ответ положительный, и AttachmentID будет доставлен, но вложение не появится в транзакции.

Вот пример раздела Attachment:

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

Если я установил RequireMtomдля Службы Истине я получу следующую ошибку:

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

С одной стороны, это работает, с другой стороны, я не уверен, будет ли оно отправлено с элементами XOP.

0 голосов
/ 10 января 2012

Хорошо, я получил данные из файла в элементе <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>

Я открою новый вопрос по этому поводу, поскольку это технически другая проблема.

Другой вопрос можно найти по адресу Ответ мыла, неправильно сформированный XML, не найдены детали XOP, используя WSE

...