Onlangs heb ik voor een aantal integraties met multipart/form-data gewerkt. Gedurende die tijd kwam ik een aantal problemen tegen en ontdekte dat alle gerelateerde artikelen op het Internet mijn vragen niet echt beantwoordden. Dus beschrijf ik in deze blog de twee belangrijkste manieren om te werken met multipart/form-data payloads in WSO2 Enterprise Integrator.
Multipart/form-data
Multipart/form-data is een van de mogelijke content typen die in HTML-formulieren worden gebruikt bij het indienen van data. Wanneer het formulier wordt ingediend, wordt er een POST-aanvraag gedaan, waarbij elk van de velden afzonderlijke “delen” van de resulterende payload zijn. Deze delen worden van elkaar gescheiden door een zogeheten “MIME boundary”.
Om een idee te geven hoe dit eruit zou komen te zien, heb ik hieronder een 2-delig voorbeeld van een multipart/form-data bericht samengesteld. Deel 1 heeft een weggelaten binaire payload, terwijl deel 2 een metadata json-object bevat.
Optie 1 – Bouw je eigen payload
Het bouwen van een multipart/form-data bericht in WSO2 Enterprise Integrator is relatief eenvoudig, maar heeft een paar nuances die problemen kunnen veroorzaken.
Om het product in staat te stellen een bericht in xml-formaat op te bouwen, moeten de juiste builder en formatter in het product ingeschakeld zijn, maar voor multipart/form-data moeten deze standaard al ingeschakeld zijn. Als je dit voor de zekerheid wilt controleren, moet het bestand axis2.xml in de map <product_home>/conf/axis2 de volgende twee regels in de secties messageBuilders en messageFormatters van het bestand bevatten.
<messageBuilder class="org.apache.axis2.builder.MultipartFormDataBuilder" contentType="multipart/form-data"/>
<messageFormatter class="org.apache.axis2.transport.http.MultipartFormDataFormatter" contentType="multipart/form-data"/>
Nu de configuratie is gecontroleerd, is het tijd om een payload te bouwen. Voor de inkomende payload zal ik de onderstaande dummy-payload gebruiken, die een afbeelding en een json-object met metadata bevat.
curl --location --request POST 'https://localhost:8243/myApi/uploadFile' \ --form 'fileContent=@"/C:/Users/Wouter/Pictures/picture.jpg"' \ --form ' fileMetadata="{\"FileName\": \" picture.jpg\", \"CorrelationId\": \"f145b832-2e5d-425d-a80b-05d5a1a5a129\", \"someNumber\": 7098813, \"Categorie\ ": \"AdditionalInfo\"}";type=applicatie/json'
Wanneer dit bericht door Enterprise Integrator in xml is ingebouwd, ziet het er als volgt uit:
<mediate> <fileContent filename=”picture.jpg”>_binary_payload_</fileContent> <fileMetadata> {"FileName": "picture.jpg", " CorrelationId": " f145b832-2e5d-425d-a80b-05d5a1a5a129 ", "someNumber": 7098813, "Category": "AdditionalInfo"} </fileMetadata> </mediate>
In de hypothetische voorbeeldintegratie wil ik berichten die het “fileMetadata” naast het “fileContent” element bevatten ergens anders heen routeren dan de berichten die alleen het “fileContent” element bevatten. Bovendien wil ik het fileMetadata element in de payload door mijn eigen json-object vervangen.
Na het uitvoeren van een filtermediator om de verschillende berichten te vinden, kan de payload op verschillende manieren worden geconstrueerd. Maar in dit voorbeeld zal ik de payloadFactory mediator gebruiken.
<payloadFactory media-type="xml">
<format>
<root
xmlns="">
<myExtraJsonContent
xmlns="http://org.apache.axis2/xsd/form-data" content-type="application/json" bestandsnaam="$2">{“dummyData”:”thisIsDummyData”}
</metadata>
<file
xmlns="http://org.apache.axis2/xsd/form-data" bestandsnaam="$2">$1
</file>
</root>
</format>
<args>
<arg evaluator="xml" expression="$body/mediate/fileContent"/>
<arg evaluator="xml" expression="$body/mediate/fileContent/@filename"/>
</args>
</payloadFactory>
In de bovenstaande payload factory kun je zien dat er twee elementen in het <root> element aanwezig zijn. Elk afzonderlijk element met de namespace “xmlns=”http://org.apache.axis2/xsd/form-data”” wordt geformatteerd als een MIME boundary die afzonderlijke MIME-delen in het resulterende uitgaande bericht van elkaar scheidt.
Elk deel kan zijn eigen content type hebben met behulp van de “content-type” attributen aan de xml-elementen. Dit kan erg belangrijk zijn, afhankelijk van hoe de backend is geconfigureerd om het bericht te lezen.
Na het construeren van de payload is de enige resterende stap het instellen van het juiste bericht- en contenttype, zodat Enterprise Integrator de payload terug kan converteren naar multipart/form-data vanuit xml.
<header name="Content-Type" scope="transport" value="multipart/form-data"/> <property name="messageType" scope="axis2" value="multipart/form-data"/>
Eenmaal ingesteld is de payload klaar om naar een backend van jouw keuze te worden verzonden.
Optie 2 – Binaire passthrough
De eerste optie is ook de eenvoudigste, maar niet geschikt voor zo veel gebruiksgevallen. Zoals de naam al doet vermoeden, wordt binaire passthrough gebruikt voor passthrough-integraties. Bijvoorbeeld wanneer een client een API aanroept en Enterprise Integrator de data alleen naar de backend doorstuurt, zonder het bericht op te bouwen.
Binaire passthrough moet worden ingeschakeld op basis van een ‘per content’ type in de axis2.xml (of deployment.toml, afhankelijk van de productversie). Het bestaat uit twee delen: de builder (die het inkomende bericht behandelt) en de formatter (die het uitgaande bericht behandelt).
Belangrijk: standaard is de xml-versie van de multipart/form-data builder/formatter van optie 1 hierboven ingeschakeld, maar deze moet worden uitgeschakeld om de binaire passthrough builder/formatter te laten werken.
Hieronder heb ik opgeschreven hoe het inschakelen hiervan er in de configuratie zelf uit zou zien.
Deployment.toml – WSO2EI 6.6.0 en nieuwer
[[custom_message_formatters]]
class = "org.wso2.carbon.relay.BinaryRelayBuilder"
content_type = "multipart/form-data"
[[custom_message_builders]]
class = "org.wso2.carbon.relay.BinaryRelayBuilder"
content_type = "multipart/form-data"
axis2.xml – WSO2EI 6.5.0 en ouder
<messageBuilders>
<messageBuilder contentType="multipart/form-data" class="org.wso2.carbon.relay.BinaryRelayBuilder"/>
</messageBuilders>
<messageFormatters>
<messageFormatter contentType="multipart/form-data" class="org.wso2.carbon.relay.ExpandingMessageFormatter"/>
</messageFormatters>
Zodra dit is gebeurd start je het product opnieuw op, waarna alle berichten met dit content type die binnenkomen niet in XML worden ingebouwd, maar gewoon als puur binair passeren. Het nadeel hiervan is, dat als er een andere integratie op dezelfde integratornode is waarvoor de multipart/form-data payload wel in xml moet worden ingebouwd, bijvoorbeeld voor een op content gebaseerd routerpatroon, het niet zal werken.
Als dat op jouw gebruiksgeval lijkt, of als je maar één integratie hebt die moet mediëren op basis van de inhoud van het multipart-bericht, raad ik je aan optie 1 nog eens te bekijken.
Ik hoop dat deze blog je op weg helpt met je integratieproject.