Introduction
I recently had a requirement to submit xml payload as a csv file embedded in an HTTP form to a REST endpoint. Based on the vendors documentation I had to achieve something similar to the below:
*REQUEST HEADER* |
|
|
|
Key |
Value |
Content-Type |
multipart/form-data; boundary=----WebKitFormBoundary123456789 |
Accept |
text/plain |
|
|
|
|
*REQUEST BODY* |
|
|
|
------WebKitFormBoundary123456789 |
Content-Disposition: form-data; name="file"; filename="Import TEST.txt" |
Content-Type: text/plain |
|
|
|
200000,55555555,01,200000,55555554,00000000100,"The Victorian Soci",00009662 |
|
|
------WebKitFormBoundary123456789-- |
This posed some issues since the standard form submission for the REST adapter auto populates the name and filename attributes of the Content-Disposition attribute with the SAP generated attachment name.
Below I have shared the possible solutions I investigated and the results obtained from each one, this will hopefully help you if facing similar issues.
Solutions
After research I found three potential methods explained below, note all screenshots are based on NWDS 7.5 and from proof of concepts I developed:
1) Use REST Receiver Adapter with Attachment attribute set and Module to format data
Since SAP PO 7.50 SP07 a new functionality is added to the REST adapter that allows sending and receiving MIME multipart messages (see SAP Note 2365727)
Ticked support Attachments and set multipart content type to ‘multipart/form-data’
Because the outputted data is not xml and to set Data Format to JSON
Set the below modules
StrictXml2PlainBean – used to convert payload to csv
MessageTranformBean – used to change the Forms Content Disposition to the required file and filename (PO sets these initially to the system generated payload name)
FormDataBean – although the ‘Support Attachment’ was set on the REST adapter it did not output the final message in an HTTP form, adding an additional form content using this bean resolved that issue.
Result: Although the above worked the filename could not be set dynamically
2) Create a new attachment with dynamic file name and do a payload swap
This is explained in blog
https://blogs.sap.com/2019/04/23/csv-multi-part-form-data-upload-as-attachment-using-http_aae-adapte...
Basically, this method uses a UDF to create a new csv attachment based on the original payload. The attachment can be added with a specified filename read from the payload/ASMA. The Payload swap bean is then used to swap the message payload with this new attachment. Again for the REST adapter the support attachments and Data Format – JSON should be set.
Result: Although a dynamic filename had been achieved for my particular scenario the form name had to be set to “file”. I tried adding the MessageTransformBean and setting
This however removed the filename element of the ContentDisposition so was not workable.
3) Create Form in Java Mapping
After reading blog
https://blogs.sap.com/2014/09/12/html-form-upload-using-http-plain-adapter-with-java-mapping/ I decided to try the Java mapping route.
Again the REST Adapter Data Format was set to JSON (XML setting complains about none valid xml)
‘Support Attachments’ unticked
The form was set on the HTTP Headers tab instead
As a proof of concept the below simplified JAVA mapping was created.
package com.mapping;
import java.io.OutputStream;
import com.sap.aii.mapping.api.AbstractTransformation;
import com.sap.aii.mapping.api.StreamTransformationException;
import com.sap.aii.mapping.api.TransformationInput;
import com.sap.aii.mapping.api.TransformationOutput;
public class FormMapping
extends AbstractTransformation {
@Override
public void transform(
final TransformationInput in,
final TransformationOutput out)
throws StreamTransformationException {
final String LINE_FEED = "\r\n";
String RESULT = "";
String boundary = "--SAP_e9943b97-7668-11ea-9604-0000005b75e1_END";
String contentDisposition = "Content-Disposition: form-data;name=\"file\";filename=\"TestFile.txt\"";
String contentType = "Content-Type: text/plain";
String content = "555555,55550000,120000,TEST SUPPLIER,11102000000000";
try {
OutputStream outputstream = out.getOutputPayload().getOutputStream();
RESULT = boundary + LINE_FEED + contentDisposition + LINE_FEED + contentType + LINE_FEED + LINE_FEED + content + LINE_FEED + LINE_FEED + boundary
+ "--" + LINE_FEED;
outputstream.write(RESULT.getBytes());
}
catch (Exception exception) {
getTrace().addDebugMessage(exception.getMessage() + RESULT);
throw new StreamTransformationException(exception.toString() + RESULT);
}
}
}
Result: The above code allows us to set both the file and filename of the Content-Disposition. This worked successfully. Note the ‘boundary’ set on the HTTP header and in the Java code must match. Not sure if the boundary needs to be dynamic or can be static? In my final code I set the boundary to be the MessageID GUID and created the csv content from the source payload.
Conclusion
There may be other ways to achieve the above but based on the three methods I tried the following conclusions can be drawn:
- For a static name and filename then method 1 can be used
- For a static name and dynamic filename then method 2 can be used
- For a dynamic name and filename then method 3 is the best solution