API Connect

 View Only
  • 1.  Converting Multipart/form-data to octet-stream content-type

    Posted Tue October 29, 2024 01:20 PM

    Please provide your inputs.

     

    My backend API has 2 operations .
    1)Operation A-This operation accepts content-type of multipart/form-data and only accepts one file as an input. So we can only send input to the backend as one file attached.

     2)Operation B-This operation accepts content-type of Octet-Stream.

     

    My API in APICONNECT has below operation. 
    1)Operation 1-which accepts multipart/formdata with multiple attachments. How can I iterate through the attachments to send one file at a time to backend api to operation A as mentioned above(is it even possible?)
    If iterating  through attachments and sending one file at a time would not work ,can we convert multipart/formdata request with 2 attachments into octet/Stream content-type and send it Backend of operation B.



    ------------------------------
    vijaya k
    ------------------------------


  • 2.  RE: Converting Multipart/form-data to octet-stream content-type

    Posted Wed October 30, 2024 09:35 AM

    @steve Linn can you please provide any inputs here.



    ------------------------------
    vijaya k
    ------------------------------



  • 3.  RE: Converting Multipart/form-data to octet-stream content-type

    Posted Fri November 01, 2024 03:34 PM

    Hi Vijaya,
    Perhaps https://chrisphillips-cminion.github.io/apiconnect/2024/07/30/multipart-API-a.html and https://chrisphillips-cminion.github.io/apiconnect/2024/07/31/multipart-API-example.html that I worked on with @Chris Phillips will be helpful.  Note the content type is multipart/related, not multipart/form-data.  With the related multipart content, the parse policy will do a nice job of moving the root body part to message.body and the root body part content-type header to message.headers.content-type, and will also take the remaining parts and make them addressable attachments in the message object.  The example simply shows using the message attachments as an array that is directly indexed, but you can always iterate over an array in Javascript to do what you need to do.  So the question would be if you have control over the inbound content-type your API will accept.  If so, simply use multipart/related and do a parse before your GatewayScript.  If not, then you try changing the content-type header to multipart/related in message.headers.content-type, taking care to not change the other attributes such a boundary, then do a parse policy before looking at the attachments.  I've not tried this myself, and the question I have is whether the parse policy will have the payload buffer in message.body so you could set the message.header.content-type header.  You might need to set x-ibm-configuration.buffering to true to ensure that as a parse policy that uses request will still have the request header asis in message as you cannot modify a request header outside of a pre-request global policy.  Some documentation that will be useful for attachment processing can be found at https://www.ibm.com/docs/en/api-connect/10.0.5.x_lts?topic=gateway-manipulating-attachments-in-message-object

    If working directly with multipart/form-data directly, in this case the parse policy will simply parse the entire multipart payload as a binary payload and it would be up to you do parse out the individual attachments.  Anything is possible, a simple matter of programming as the old saying goes, where you could get the boundary from the content-type header (you might need to find that in request.original.headers.content-type after a parse policy is used) and using the boundary from the content-type header, doing a buffer.indexOf to find each boundary, then another to find the <cf><lf> that would mark the end of the part headers to get to the payload, and another to search for the -- and boundary to find then end of the part to extract the payload.  All this has to be done with buffers as any conversion to string will encode your string and change any binary data. So it is doable, but it takes some development.  I did something like this years ago in the v5 days and used the GatewayScript debugger to step through my code to get it working, so although the process seems straightforward enough, the good GDB debugger sped things up significantly.

    Hope this helps!
    Steve Linn



    ------------------------------
    Steve Linn
    Senior Consulting I/T Specialist
    IBM
    ------------------------------



  • 4.  RE: Converting Multipart/form-data to octet-stream content-type

    Posted 26 days ago

    Hi Steve,

    We tried to use the split() function and able to print the boundary values in the log. However, when we are testing with multiple files using any test client like ARC, in response we are seeing both the file seperated by boundaries but in log when we are printing context.get('message.body'), it is not printing the whole response instead only one attachement we can see.

    Also, not able to seperate the file details in the payload depending upon the boundary values using indexOf(). Can you please help on this?



    ------------------------------
    Nirmalya Mukherjee
    ------------------------------



  • 5.  RE: Converting Multipart/form-data to octet-stream content-type

    Posted 26 days ago
    Edited by vijaya k 26 days ago

    Steve,

    Below is the code which Nirmalaya is talking about, we are testing multipart/form-data  request with 2 attachments.

    below is the request, we would like to seperate each file content when we receive and send it as an octet/stream or each attachment as multipartform-data request to backend

    request

      curl -X POST "https://testurl"  -H "Content-Type: multipart/form-data" -F "file=@testsampleapicfile2.com;type=application/octet-stream" -F "file=@testsampleapic.com;type=application/octet-stream" -H "Client-Id: xyz"
    code
     var contenttype=context.get("request.headers.Content-Type");
    var reqbody=context.get("request.body");
    console.error("contenttype value is"+contenttype);
    console.error("reqbody is"+reqbody);
    if (contenttype.includes('multipart/form-data')) {
        
      
          var boundary = contenttype.split(';')[1];
          console.error("boundary"+boundary);
          var boundary1 = boundary.split('=')[1];
            console.error("boundary1"+boundary1);    
          
        var indexofboundary=  reqbody.indexOf(boundary1);
    var indexofpayload1=  reqbody.indexOf('<cf><lf>');
     
     console.error("indexofpayload"+indexofboundary);     
      console.error("indexofpayload456"+indexofpayload1);  
      
     
    }

    Below is the screenshot for console.error("reqbody is"+reqbody);

     screenshot of console.error("indexofpayload456"+indexofpayload1);  
      



    ------------------------------
    vijaya k
    ------------------------------



  • 6.  RE: Converting Multipart/form-data to octet-stream content-type

    Posted 26 days ago

    Hi Vijaya and Nirmalaya,
    A great reason to use the GatewayScript debugger to triage your code :-) There was a previous post to this forum that showed you could do a set-variable policy to set a specific variable to enable the GatewayScript debugger.  You'd then need to add a debugger; statement to your code as the first breakpoint.  If you have CLI access to your DataPower, then you can get into the debugger via a SSH session to your appliance, but if not, there is also a remote debugger where you could do a chrome://inspect (assuming the remote debugger is enabled - check your default domain configuration).  Definitely an invaluable tool in not only testing out the code you've written, but if a statement doesn't work, using the print command of the debugger, you can try other statements that find out what your correct code should have been.  Just a public service announcement :-) 

    reqbody.indexOf('<cf><lf>'); has a value of -1 (Not found) since that 8 character string is not in your payload.  To get to the start of your payload, you would need to code

    reqbody.indexOf('\r\n\r\n') + 4;

    Note there are two carriage return linefeeds after your last part header.  You add 4 to that index to get to the start of your part payload as indexOf will return the position of the first character of your search string.  You'll need to also get the index of the start of the next boundary (don't forget you have two -- characters before the boundary) which should then provide you the start and end positions of your payload.  Buffer supports a buffer.slice(start,end) which would allow you to set a variable to just the payload.

    As for my code that extracts the boundary from the contentType header, I'm partial to a regular expression 

    let boundary = ctype.match(/boundary="?([^" ]+)/)[1];

    as I've seen the boundary attribute sometimes enclosed in double quotes and this would handle it all in one statement.  In your case using the split function on the contentType string works in multiple statements

    Content-Type: multipart/form-data; boundary=------------someboundary

    var boundary = contenttype.split(';')[1]; Will get you everything after the semi colon, ie the boundary attribute, and the
    var boundary1 = boundary.split('=')[1]; Will get you the actual boundary (assuming it is not enclosed in quotes)

    Looking at your log of reqbody (of course the concatenation in the console.error will convert your buffer to a string and will utf-8 encode any binary data)  I'm seeing in your log what appears to be two files, the first filename testsampleapicfie2.com and the second testsampleapic.com so your log of the request is showing both files present.

    If you're confident you'll only have two parts you can repeat extracting out the different parts, but I'd personally put this in a while loop where you would get the index of your first boundary, loop while you have a value of that boundary, extract your payload and use urlopen.open to send it as you wish.  Then reset the boundary index used in the while to the location of your boundary after the end of your current payload, etc.  You'd need to also in that while to check that what follows your boundary to ensure it isn't just a -- which will indicate an end of your form-data.

    Hope this helps,
    Best Regards,
    Steve Linn


    ------------------------------
    Steve Linn
    Senior Consulting I/T Specialist
    IBM
    ------------------------------