fb
WSO2 5 min

Datei-Uploads mit multipart/form-data über WSO2 EI/ESB

Wouter van Wijngaarden
Wouter van Wijngaarden
Integration Consultant
Social Share Image
Scrollen

In letzter Zeit habe ich für einige Integrationen mit multipart/form-data gearbeitet. Dabei stieß ich auf ein paar Probleme und stellte fest, dass es im Internet keinen Artikel zu diesem Thema gibt, der meine Fragen wirklich beantworten kann. In diesem Blog beschreibe ich die beiden wichtigsten Methoden für die Arbeit mit multipart/form-data-Nutzlasten im WSO2 Enterprise Integrator.

Multipart/form-data

Multipart/form-data ist einer der möglichen Inhaltstypen, die in HTML-Formularen beim Senden von Daten verwendet werden. Beim Absenden des Formulars wird eine POST-Anfrage gestellt, bei der jedes einzelne Feld einen separaten „Teil“ der resultierenden Nutzlast darstellt. Diese Teile sind durch eine sogenannte „Mime Boundary“ getrennt.

Um zu veranschaulichen, wie das aussehen würde, habe ich unten beispielhaft eine zweiteilige multipart/form-data-Nachricht erstellt. Teil 1 enthält eine weggelassene binäre Nutzlast und Teil 2 enthält ein Metadaten-JSON-Objekt.

Multipart form data 1

Möglichkeit 1 – Erstellen Sie Ihre eigene Nutzlast

 Das Erstellen einer multipart/form-data-Nachricht im WSO2 Enterprise Integrator ist relativ einfach, weist jedoch einige Nuancen auf, die Probleme verursachen können.

Damit das Produkt Nachrichten im XML-Format erstellen kann, müssen der richtige Builder und Formatter im Produkt aktiviert sein. Für multipart/form-data sollten diese bereits standardmäßig aktiviert sein. Um das zu überprüfen, sollte die Datei axis2.xml im Ordner <product_home>/conf/axis2 die folgenden beiden Zeilen in den Abschnitten messageBuilders und messageFormatters der Datei enthalten.

<messageBuilder class="org.apache.axis2.builder.MultipartFormDataBuilder" contentType="multipart/form-data"/>
<messageFormatter class="org.apache.axis2.transport.http.MultipartFormDataFormatter" contentType="multipart/form-data"/>

Da die Konfiguration nun überprüft wurde, wird es Zeit, eine Nutzlast zu erstellen. Für die eingehende Nutzlast verwende ich die unten aufgeführte Dummy-Nutzlast, die ein Bild und ein JSON-Objekt mit Metadaten enthält.

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, \"Category\": \"AdditionalInfo\"}";type=application/json'

 Wenn diese Nachricht vom Enterprise Integrator in XML umgewandelt wird, sieht das so aus:

<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 der hypothetischen Beispielintegration möchte ich Nachrichten, die neben dem Element „fileMetadata“ auch das Element „fileContent“ enthalten, an eine andere Stelle weiterleiten als Nachrichten, die nur das Element „fileContent“ enthalten. Außerdem möchte ich das Element „fileMetadata“ in der Nutzlast durch mein eigenes JSON-Objekt ersetzen.

Nachdem ein Filter-Mediator die verschiedenen Nachrichten gefunden hat, kann die Nutzlast auf unterschiedliche Arten erstellt werden. Aber für dieses Beispiel verwende ich den payloadFactory-Mediator.

<payloadFactory media-type="xml">
	<format>
		<root
			xmlns="">
			<myExtraJsonContent
				xmlns="http://org.apache.axis2/xsd/form-data" content-type="application/json" filename="$2">{“dummyData”:”thisIsDummyData”}
			</metadata>
			<file
				xmlns="http://org.apache.axis2/xsd/form-data" filename="$2">$1
			</file>
		</root>
	</format>
	<args>
		<arg evaluator="xml" expression="$body/mediate/fileContent"/>
		<arg evaluator="xml" expression="$body/mediate/fileContent/@filename"/>
	</args>
</payloadFactory>

In der oben dargestellten Payload-Factory sehen Sie zwei Elemente innerhalb des Elements <root>. Jedes einzelne Element mit dem Namensraum „xmlns=“http://org.apache.axis2/xsd/form-data““ wird in der resultierenden ausgehenden Nachricht als separater MIME-Teil der MIME Boundary formatiert.

Jeder Teil kann über die „content-type“-Attribute der XML-Elemente auch einen eigenen Inhaltstyp haben. Das kann, je nachdem, wie das Backend zum Lesen der Nachricht konfiguriert ist, sehr wichtig sein.

Nach dem Erstellen der Nutzlast brauchen Sie nur noch die richtige Nachricht und den richtigen Inhaltstyp festzulegen, damit der Enterprise Integrator die Nutzlast von XML zurück in multipart/form-data konvertieren kann.

<header name="Content-Type" scope="transport" value="multipart/form-data"/>
<property name="messageType" scope="axis2" value="multipart/form-data"/>

 Sobald die Nutzlast eingerichtet ist, kann sie an ein Backend Ihrer Wahl gesendet werden.

Möglichkeit 2 – Binäres Passthrough

Die erste Möglichkeit ist auch die einfachste, eignet sich aber nicht für alle Anwendungsfälle. Wie der Name schon sagt, wird binäres Passthrough für Passthrough-Integrationen verwendet. Beispiel: Ein Client ruft eine API auf und der Enterprise Integrator leitet die Daten nur an das Backend weiter, ohne die Nachricht zu erstellen.

Das binäre Passthrough muss auf der Basis des Typs „per content“ in der Datei axis2.xml (oder je nach Produktversion in deployment.toml) aktiviert werden. Es besteht aus zwei Teilen – dem Builder (der die eingehende Nachricht verarbeitet) und dem Formatter (der die ausgehende Nachricht verarbeitet).

Wichtig: Standardmäßig ist die XML-Version des Builders/Formatters für multipart/form-data aus der oben genannten Möglichkeit 1 aktiviert. Diese müssen deaktiviert werden, damit der binäre Passthrough-Builder/Formatter funktioniert.

Nachfolgend habe ich aufgeschrieben, wie das Aktivieren in der Konfiguration selbst aussehen würde.

Deployment.toml – WSO2EI 6.6.0 und neuer

[[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 und älter

<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> rt/form-data" class="org.wso2.carbon.relay.ExpandingMessageFormatter"/> </messageFormatters> 

Wenn das erledigt ist, starten Sie das Produkt neu. Alle mit diesem Inhaltstyp eingehenden Nachrichten werden dann nicht in XML umgewandelt, sondern als reines Binärformat weitergeleitet. Der Nachteil dabei ist, dass eine andere Integration auf demselben Integrator-Knoten, bei der die multipart/form-data-Nutzlast in XML umgewandelt werden muss (beispielsweise für ein inhaltsbasiertes Router-Muster), nicht funktioniert.

Sollte das Ihr Anwendungsfall sein oder sollten Sie nur eine einzelne Integration haben, die basierend auf dem Inhalt der mehrteiligen Nachricht vermitteln muss, empfehle ich Ihnen, noch einmal auf Möglichkeit 1 zurückzukommen.

Ich hoffe, dieser Blog hilft Ihnen bei Ihrem Integrationsprojekt.