1. Steve Sargent
  2. PowerBuilder
  3. Wednesday, 21 April 2021 14:51 PM UTC

I'm converting a web service client call from SOAP to HTTPClient using 2019 R3. The response back from the web service is multipart which means my response contains both XML and binary content. The SOAP implementation gave me a property in the response to pull the binary data into a BLOB. However, now that I'm using HTTPClient the response BLOB it seems I'll have to write code to parse the BLOB response body to get the binary data. 

 

Does anybody have any example PB code of how to extract the binary portion of multipart data from the HTTPClient response body?

 

 

Daryl Foster Accepted Answer Pending Moderation
  1. Monday, 26 April 2021 03:47 AM UTC
  2. PowerBuilder
  3. # 1

Hi Steve,

I've attached a library with a couple of objects which should parse a multipart response. The objects aren't commented but hopefully they are straightforward enough.  If something isn't clear just let me know.

I've borrowed the of_ParseToArray function from pfc_n_cst_string and included it in the n_cst_multipart_part object to make it self contained, but if you already have a string array parsing function in your application you could remove that and replace it with your own version.

I've tested it on a few multipart blobs and it seems to work ok. It even parsed a few email eml files and successfully extracted all the parts.  Performance wise it seems ok on smaller files, I've been testing on attachments that are around 500KB and it works well.  I've also tested it with 3, 10 and 20MB attachments and the performance is ok, but not great, particularly on the 20MB attachment.

The main function is the of_Parse(blob, string) method of the n_cst_multipart object.  You need to pass in your http response blob and the multipart boundary.  There is a function called of_GetBoundary(string) on the n_cst_multipart object that will return the boundary string from a content-type header that you pass in.  If you've already extracted the boundary string, you'll just need to remember to prepend '--' to the string.  The of_GetBoundary function already does that.

If successful, the of_Parse function will set a property called io_parts which will be an array of the parts extracted.  The parts are of type n_cst_multipart_part.  Each part includes the blob data and the headers for that part.

If you aren't too familiar with application/xop+xml content, the http response will include a first part which should be your xml and then zero or more attachments.  In your example it just includes the one attachment, but depending on your SOAP service it may return no attachments, or multiple attachments, based on the SOAP request.

Each attachment should be referenced in the XML based on an xop:Include tag (e.g. <xop:Include href="cid:1f7d0d16-dc0d-4a69-b9db-4f142e051ef7-2@cxf.apache.org">)  You can find the corresponding attachment by looking for the Content-ID header in the individual parts (e.g. Content-ID: <1f7d0d16-dc0d-4a69-b9db-4f142e051ef7-2@cxf.apache.org>).  If all your SOAP calls just return one attachment though you can just skip to the second part parsed by the multipart parser and deal with it.

Here is an example of how you could use it to extract the blob data from your example:


HttpClient lo_HttpClient
integer li_rc
blob lblb_response
blob lblb_xml
blob lblb_attachment
n_cst_multipart lo_multipart
string ls_content_type
string ls_content_disposition
string ls_content_id

// Some stuff has happened before this to call webservice

li_rc = lo_HttpClient.GetResponseBody(lblb_response)
if li_rc = 1 then
    ls_content_type = lo_HttpClient.GetResponseHeader("Content-Type")
    if ls_content_type <> '' then
        ls_boundary = lo_multipart.of_GetBoundary(ls_content_type)
        if ls_boundary <> '' then
            li_parts = lo_multipart.of_Parse(lblb_response, ls_boundary)

            // Specific to this example we are expecting 2 parts,
            // but you could loop through the parts and get what you want
            if li_parts = 2 then
                // Get the blob data for the two parts
                lblb_xml = lo_multipart.io_parts[1].iblb_data
                lblb_attachment = lo_multipart.io_parts[2].iblb_data

                // You can use the headers from each part to determine the file type,
                // filename etc
                ls_content_type = lo_multipart.io_parts[2].of_GetHeader('Content-Type')
                ls_content_disposition = lo_multipart.io_parts[2].of_GetHeader('Content-Disposition')
                ls_content_id = lo_multipart.io_parts[2].of_GetHeader('Content-ID')
            else
                // An error getting the two parts of the response
            end if        
        else
            // Error getting boundary from the response content type header
        end if
    else
        // An error getting the response content type header
    end if
else
    // An error getting the response
end if


Good luck with it, hopefully this is helpful to you.  Let us know how you go.

Attachments (1)
Comment
  1. Daryl Foster
  2. Wednesday, 27 September 2023 22:34 PM UTC
Thanks Mike, I just saw it on CodeExchange. It might be worth mentioning in your description that it should work with any multipart MIME messages, not just REST. The other obvious one that people may need it for is XOP from SOAP (Content-Type: application/xop+xml;)
  1. Helpful
  1. mike S
  2. Thursday, 28 September 2023 12:32 PM UTC
can you add that as a comment?

I can try to edit the description, but every time i tried to edit something, codeXchange removes if until the changes get approved. I have an updated version that generates the multi-part message from the parts that i haven't posted up either because of that.
  1. Helpful
  1. Daryl Foster
  2. Friday, 6 October 2023 06:50 AM UTC
Hi Mike, sorry about the late reply. I've just added a comment to your CodeXchange post.
  1. Helpful 2
There are no comments made yet.
Steve Sargent Accepted Answer Pending Moderation
  1. Thursday, 22 April 2021 15:23 PM UTC
  2. PowerBuilder
  3. # 2

Thanks for the replies. Below is more info showing the response data I'm getting from the web service.

 

Here is the Content-Type in the header of the response. This specifies the Boundary.

multipart/related; boundary="uuid:98b5fe92-4e67-44b8-bfac-27a81ef5a90f"; start-info="text/xml"; type="application/xop+xml"; start="<root.message@cxf.apache.org>"

 

Below is the body of the response with placeholder added where the binary data exists.

--uuid:87593bac-7d35-4725-9265-1a87627b6bc7
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: binary
Content-ID: <root.message@cxf.apache.org>

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:retrieveResponse xmlns:ns2="http://services.web.ecs.dot.pa.gov/">
<return>
<document>
<content>
<xop:Include href="cid:1f7d0d16-dc0d-4a69-b9db-4f142e051ef7-2@cxf.apache.org" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
</content>
</document>
</return>
</ns2:retrieveResponse>
</soap:Body>
</soap:Envelope>

--uuid:87593bac-7d35-4725-9265-1a87627b6bc7
Content-Type: application/pdf
Content-Transfer-Encoding: binary
Content-ID: <1f7d0d16-dc0d-4a69-b9db-4f142e051ef7-2@cxf.apache.org>
Content-Disposition: attachment;name="A500616369.pdf"

%PDF-1.4
%

[binary data is here]


%%EOF

--uuid:98b5fe92-4e67-44b8-bfac-27a81ef5a90f--

Comment
  1. Daryl Foster
  2. Friday, 23 April 2021 12:02 PM UTC
Cool, that makes sense, I’ve got a basic multipart parser I’ve written in Powerbuilder. It hasn’t been heavily tested and It’s undocumented and uncommented. But I can post it next week if you are interested. I haven’t tested it on a real http response, but it should take your response and split it into two parts. The first part would give you a blob with your xml and the second part would give you a blob with your attachment.
  1. Helpful
  1. Steve Sargent
  2. Friday, 23 April 2021 12:06 PM UTC
Thanks a lot Daryl, that would be much appreciated!
  1. Helpful
  1. Daryl Foster
  2. Friday, 23 April 2021 12:22 PM UTC
No worries, if I don’t get a chance over the weekend, I’ll post it on Monday.
  1. Helpful
There are no comments made yet.
Mark Lee @Appeon Accepted Answer Pending Moderation
  1. Thursday, 22 April 2021 08:03 AM UTC
  2. PowerBuilder
  3. # 3

Hi Steve,


For how to use HTTPClient to get the BLOB response body, you can refer to the methods below:

objectname.GetResponseBody ( blob data )https://docs.appeon.com/pb2019r3/powerscript_reference/ch02s04s320.html objectname.ReadData ( data, bufferSize )https://docs.appeon.com/pb2019r3/powerscript_reference/ch02s04s611.html  

 

BTW, I am not sure how you receive a multi-part message from SOAP in PowerBuilder.

As Daryl said, can you share a copy of a multipart response you receive?  

How do you set the Content Type content in your Web Server request? Can you provide a sample test case for us for more study?

 

Regards,

 

Comment
  1. Kevin Ridley
  2. Thursday, 22 April 2021 12:03 PM UTC
Agreed, I've done multi part request but never response. Maybe there is an XML element that contains binary data?
  1. Helpful
There are no comments made yet.
René Ullrich Accepted Answer Pending Moderation
  1. Thursday, 22 April 2021 05:34 AM UTC
  2. PowerBuilder
  3. # 4

Hi Steve,

I have an implementation to send multipart body but not to receive.

But I think it would not be difficult.

You need to get the "Content-Type" header. It also contains a "boundary" string (e.g. something like "multipart/form-data; boundary=xxxxxxx"). You will need the boundary (in this example "xxxxxxx") to split the parts of the response body.

In the response body each part starts with "--" and the boundary and a CRLF.

It follows a line with information about gthe content of this part (e.g. Content-Disposition: form-data; name="meta") that ends with CRLF.

It follows an empty line (only CRLF).

In the next line starts the content of the part. The parts ends with CRLF and "--", the boundary string (e.g. --xxxxxxx) and CRLF. This boundary is the start of the next part.

After last part it ends with CRLF, "--", the boundary string, again "--" (e.g. --xxxxxxx--) and CRLF.

 

The challenge is to split the body if you need to have it in a blob because you have no Pos function.

HTH,

René

 

 

Comment
There are no comments made yet.
Daryl Foster Accepted Answer Pending Moderation
  1. Thursday, 22 April 2021 00:13 AM UTC
  2. PowerBuilder
  3. # 5

Hi Steve,

 

Is it possible to share a copy of a multipart response you receive?  Does it come back as Content Type application/xop+xml?

I don't think I've ever parsed a multi-part message in Powerbuilder, but it should be possible.  Depending on the format of the multi-part message, I would probably look at writing something in C# using one of the available mime parsers and then call it from Powerbuilder using DotNetAssembly and DotNetObject.

Comment
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.