Recently, I came across an interesting integration use-case; an “API” that actually expected a multipart/form-data MIME type rather than the more conventional application/json or application/xml.

Well, this triggered a sense of overwhelming nostalgia.

It took me back to a time where we used web pages to complete forms which we submitted to the server as an HTML POST. The more complex forms on these web pages would even allow you to submit a file…

… with something like:

 

<input type="file"/>
 
Enter the multipart/form-data MIME type!

This allowed client applications like Web Browsers to submit both their form content and one or more file uploads in a single request.

Having not used this mechanism for over a decade, imagine my surprise when I encountered an “API” that required a multipart/form-data submission. It’s not completely crazy, but it is certainly unusual.

Come to think of it, I don’t believe I have ever had to submit content in this format from a Mule project. As such, I had two challenges:

One; jog my rusty memory and get my head back into HTML Form submission and…

Two; check whether the current version of Mule and Dataweave  actually support this content type.

I whispered…

And the Mule delivered!

Of course it supports the format. In fact it was relatively easy. See the simple flow below

 

In the above flow, we perform the following steps:

  • Fetch a token for authorisation (to be used later in the HTTP Request)
  • Build the Multipart Form-Data Request
  • A bit of logging
  • Send HTTP Request to the remote server 

The crucial step is the Transform in step two

 

%dw 2.0
output multipart/form-data
boundary='24f083ef8ec51ef6819c327544f73fa5'
---
{
   parts : {
      field1 : {
         headers : {
            "Content-Disposition" : {
               "name": "firstName"
            }
         },
         content : "Jane"
      },
      field2 : {
         headers : {
            "Content-Disposition" : {
               "name": "surname"
            }
         },
         content : "Smith"
      },
      field3 : {
         headers : {
            "Content-Disposition" : {
               "name": "tel"
            }
         },
         content : "555-5678"
      },
      file1 : {
         headers : {
            "Content-Disposition" : {
               "name": "cv_file",
               "filename": "cv_jane_smith.pdf"
            },
            "Content-Type" : payload.^mimeType // E.g. "application/pdf"
         },
         content : payload
      }
   }
}

In the above Dataweave script, we start with the output type: multipart/form-data. We also need to define a boundary. This is a separator value that tells the server where each unique part (i.e. unique form field) starts and ends. We do this with the boundary=’24f083ef8ec51ef6819c327544f73fa5′ directive in the output instruction. Your boundary can be any value you like, as long as it is unique within the message body. Also, you don’t need to change it between calls; it can be a static value.

Now, on to the content itself. It all starts with the parts node. All of your form fields (i.e. form parts) will go inside this object.

Fields 1-3 are normal form fields (i.e. text). Each field has the following properties:

  • Headers
  • Content

For your standard form fields, all you really need is field name. In HTML this would look something like <input name=”firstName”/>. The name value is placed in the field.headers.Content-Disposition.name property. This tells the server which field the content part belongs to. The value simply goes in the field.content property. Like so…

 

field1 : {
         headers : {
            "Content-Disposition" : {
               "name": "firstName"
            }
         },
         content : "Jane"
      }

 

File fields are a little different. While the content property is more or less the same, your headers need a little more information.

First, you need to add field.headers.Content-Disposition.filename in the Content-Disposition node, in addition to the field.headers.Content-Disposition.name property.

Second, you need to add an additional header; field.headers.Content-Type. This String value informs the server of the MIME type for the given message part (i.e. the given file). It can be any valid MIME type. The example uses application/pdf. It could just as easily be image/jpg for an image upload.

 

file1 : { 
   headers : { 
      "Content-Disposition" : { 
         "name": "cv_file",
         "filename": "cv_jane_smith.pdf"
      },
      "Content-Type" : payload.^mimeType // E.g. "application/pdf" 
   },
   content : payload 
}
 
Whisper!

If your target application supports multiple MIME types, you can snatch the actual MIME type of your payload as follows: payload.^mimeType

And there you have it. Your message body is ready to send. Simply configure your request as follows:

 

 

On the MIME Type tab, select multipart/form-data.

 

 

Finally, for my endpoint, I added the required Authorization header. Of course your target endpoint may not require this authorisation header.

 

 

And that’s it! Submitting multipart form data is actually quite easy. 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>