Knowledge Base

Find answers to common questions about Cloudmersive products and services.



Scanning an XML String in MuleSoft with Cloudmersive Virus Scan
10/15/2025 - Cloudmersive Support


In this flow, we take in XML as a x-www-form-urlencoded string with parameter name xml. This string is then sent to the Cloudmersive Virus Scan API for virus scanning. We can choose between the Advanced Virus Scan and Basic Virus Scan. Note that you should replace REPLACE_WITH_YOUR_CLOUDMERSIVE_API_KEY.

Scan XML String for Viruses in MuleSoft

Here is the full Flow XML for the Mule 4 flow:

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
      xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core"
      xmlns:http="http://www.mulesoft.org/schema/mule/http"
      xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="
        http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
        http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
        http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd">

    <!-- Listener on 8081 -->
    <http:listener-config name="listener" doc:name="Listener (8081)">
        <http:listener-connection host="0.0.0.0" port="8081"/>
    </http:listener-config>

    <!-- Health check -->
    <flow name="ping">
        <http:listener doc:name="GET /ping" config-ref="listener" path="/ping" allowedMethods="GET"/>
        <set-payload value="OK"/>
    </flow>

    <!-- Cloudmersive request config -->
    <http:request-config name="cloudmersive" doc:name="Cloudmersive">
        <http:request-connection protocol="HTTPS" host="api.cloudmersive.com" port="443"/>
    </http:request-config>

    <!-- POST /scan-xml -->
    <flow name="scan-xml-flow">
        <http:listener doc:name="POST /scan-xml" config-ref="listener" path="/scan-xml" allowedMethods="POST">
            <http:response statusCode="#[vars.httpStatus default 200]">
                <http:headers><![CDATA[#[{'Content-Type':'application/json'}]]]></http:headers>
            </http:response>
            <http:error-response statusCode="#[vars.httpStatus default 500]">
                <http:headers><![CDATA[#[{'Content-Type':'application/json'}]]]></http:headers>
            </http:error-response>
        </http:listener>

        <logger level="INFO"
                message="scan-xml invoked. Content-Type: #[attributes.headers.'content-type'], inbound type: #[typeOf(payload)]"/>

        <flow-ref name="process-scan-flow"/>
    </flow>

    <!-- POST /api/scan-xml (same processing) -->
    <flow name="scan-xml-with-basepath-flow">
        <http:listener doc:name="POST /api/scan-xml" config-ref="listener" path="/api/scan-xml" allowedMethods="POST">
            <http:response statusCode="#[vars.httpStatus default 200]">
                <http:headers><![CDATA[#[{'Content-Type':'application/json'}]]]></http:headers>
            </http:response>
            <http:error-response statusCode="#[vars.httpStatus default 500]">
                <http:headers><![CDATA[#[{'Content-Type':'application/json'}]]]></http:headers>
            </http:error-response>
        </http:listener>

        <logger level="INFO"
                message="/api/scan-xml invoked. Content-Type: #[attributes.headers.'content-type'], inbound type: #[typeOf(payload)]"/>

        <flow-ref name="process-scan-flow"/>
    </flow>

    <!-- Shared processing -->
    <flow name="process-scan-flow">

        <!-- Normalize headers and basic flags -->
        <set-variable variableName="contentType" value="#[lower((attributes.headers.'content-type' default '') as String)]"/>
        <set-variable variableName="isForm" value="#[startsWith(vars.contentType, 'application/x-www-form-urlencoded')]"/>

        <!-- Normalize to String -->
        <choice doc:name="Normalize to String">
            <!-- FORM: application/x-www-form-urlencoded -->
            <when expression="#[vars.isForm]">

                <!-- Always build a safe form map first; never dereference payload directly -->
                <ee:transform doc:name="Ensure formMap">
                    <ee:variables>
                        <ee:set-variable variableName="formMap"><![CDATA[%dw 2.0
output application/java
---
payload match {
  case s is String -> read(s, "application/x-www-form-urlencoded")
  case b is Binary -> read((b as String { encoding: "UTF-8" }), "application/x-www-form-urlencoded")
  case o is Object -> o
  else -> {}
}]]></ee:set-variable>
                    </ee:variables>
                </ee:transform>

                <!-- Require 'xml' or 'input' (quoted because 'input' is reserved) -->
                <choice>
                    <when expression="#[((vars.formMap.'xml' default null) == null) and ((vars.formMap.'input' default null) == null)]">
                        <raise-error type="VALIDATION:INVALID_PAYLOAD"
                                     description="Missing form field 'xml' (or 'input') for application/x-www-form-urlencoded."/>
                    </when>
                    <otherwise>
                        <set-variable variableName="xmlString"
                                      value="#[(vars.formMap.'xml' default vars.formMap.'input') as String]"/>
                    </otherwise>
                </choice>
            </when>

            <!-- Non-form: String -->
            <when expression="#[not vars.isForm and (payload is String)]">
                <set-variable variableName="xmlString" value="#[payload]"/>
            </when>

            <!-- Non-form: Binary -->
            <when expression="#[not vars.isForm and (payload is Binary)]">
                <set-variable variableName="xmlString" value="#[(payload as String { encoding: 'UTF-8' })]"/>
            </when>

            <!-- Non-form: Mule parsed it (Object) -> serialize back to XML -->
            <otherwise>
                <set-variable variableName="xmlString" value="#[write(payload, 'application/xml')]"/>
            </otherwise>
        </choice>

        <!-- Optional filename from query param -->
        <set-variable variableName="fileName" value="#[attributes.queryParams.fileName default 'input.xml']"/>

        <!-- Build multipart/form-data payload (part name must be 'inputFile') -->
        <ee:transform doc:name="Build multipart/form-data body">
            <ee:message>
                <ee:set-payload><![CDATA[%dw 2.0
output multipart/form-data
---
{
  parts: {
    inputFile: {
      headers: {
        // Use a single string form for Content-Disposition
        "Content-Disposition": "form-data; name=\"inputFile\"; filename=\"" ++ (vars.fileName as String) ++ "\"",
        // IMPORTANT: treat content as raw bytes, not XML
        "Content-Type": "application/octet-stream"
      },
      // Send the XML text as Binary to avoid XML writer
      content: (vars.xmlString as Binary { encoding: "UTF-8" })
    }
  }
}
]]></ee:set-payload>
            </ee:message>
        </ee:transform>

        <!-- Call Cloudmersive -->
        <http:request doc:name="Scan via Cloudmersive"
                      method="POST"
                      config-ref="cloudmersive"
                      path="/virus/scan/file">
            <http:headers><![CDATA[#[{
                Apikey: 'REPLACE_WITH_YOUR_CLOUDMERSIVE_API_KEY',
                Accept: 'application/json'
            }]]]></http:headers>
        </http:request>

        <!-- Return upstream JSON -->
        <ee:transform doc:name="Respond JSON">
            <ee:message>
                <ee:set-payload><![CDATA[%dw 2.0
output application/json
---
payload
]]></ee:set-payload>
            </ee:message>
        </ee:transform>

        <!-- Errors -->
        <error-handler>
            <on-error-propagate type="VALIDATION:*" doc:name="400 Bad Request">
                <set-variable variableName="httpStatus" value="400"/>
                <ee:transform doc:name="Body 400">
                    <ee:message>
                        <ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
  error: "Bad Request",
  message: (error.description default "Validation error.")
}
]]></ee:set-payload>
                    </ee:message>
                </ee:transform>
            </on-error-propagate>

            <on-error-propagate type="HTTP:*" doc:name="502 Upstream error">
                <set-variable variableName="httpStatus" value="502"/>
                <ee:transform doc:name="Body 502">
                    <ee:message>
                        <ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
  error: "Upstream error",
  upstreamType: error.errorType.identifier,
  message: error.description
}
]]></ee:set-payload>
                    </ee:message>
                </ee:transform>
            </on-error-propagate>

            <on-error-propagate doc:name="500 Fallback">
                <set-variable variableName="httpStatus" value="500"/>
                <ee:transform doc:name="Body 500">
                    <ee:message>
                        <ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
  error: (error.errorType.identifier default "UNKNOWN"),
  message: (error.description default "Unexpected error")
}
]]></ee:set-payload>
                    </ee:message>
                </ee:transform>
            </on-error-propagate>
        </error-handler>
    </flow>
</mule>

600 free API calls/month, with no expiration

Sign Up Now or Sign in with Google    Sign in with Microsoft

Questions? We'll be your guide.

Contact Sales