import ballerina/http;
import ballerina/log;
import ballerina/mime;http:Client clientEP = new("http://localhost:9090");@http:ServiceConfig {
    basePath: "/multiparts"
}
service multipartDemoService on new http:Listener(9090) {    @http:ResourceConfig {
        methods: ["POST"],
        path: "/decode"
    }
    resource function multipartReceiver(http:Caller caller, http:Request
                                        request) {
        http:Response response = new;
        var bodyParts = request.getBodyParts();
        if (bodyParts is mime:Entity[]) {
            foreach var part in bodyParts {
                handleContent(part);
            }
            response.setPayload(<@untainted> bodyParts);
        } else {
            log:printError(<string> bodyParts.reason());
            response.setPayload("Error in decoding multiparts!");
            response.statusCode = 500;
        }
        var result = caller->respond(response);
        if (result is error) {
            log:printError("Error sending response", err = result);
        }
    }    @http:ResourceConfig {
        methods: ["GET"],
        path: "/encode"
    }
    resource function multipartSender(http:Caller caller, http:Request req) {
        mime:Entity jsonBodyPart = new;
        jsonBodyPart.setContentDisposition(
                        getContentDispositionForFormData("json part"));
        jsonBodyPart.setJson({"name": "wso2"});
        mime:Entity xmlFilePart = new;
        xmlFilePart.setContentDisposition(
                       getContentDispositionForFormData("xml file part"));
        xmlFilePart.setFileAsEntityBody("./files/test.xml",
                                        contentType = mime:APPLICATION_XML);
        mime:Entity[] bodyParts = [jsonBodyPart, xmlFilePart];
        http:Request request = new;
        request.setBodyParts(bodyParts, contentType = mime:MULTIPART_FORM_DATA);
        var returnResponse = clientEP->post("/multiparts/decode", request);
        if (returnResponse is http:Response) {
            var result = caller->respond(returnResponse);
            if (result is error) {
                log:printError("Error sending response", err = result);
            }
        } else {
            http:Response response = new;
            response.setPayload("Error occurred while sending multipart
                                    request!");
            response.statusCode = 500;
            var result = caller->respond(response);
            if (result is error) {
                log:printError("Error sending response", err = result);
            }
        }
    }
}
function handleContent(mime:Entity bodyPart) {
    var mediaType = mime:getMediaType(bodyPart.getContentType());
    if (mediaType is mime:MediaType) {
        string baseType = mediaType.getBaseType();
        if (mime:APPLICATION_XML == baseType || mime:TEXT_XML == baseType) {
            var payload = bodyPart.getXml();
            if (payload is xml) {
                log:printInfo(payload.toString());
            } else {
                log:printError(<string> payload.detail().message);
            }
        } else if (mime:APPLICATION_JSON == baseType) {
            var payload = bodyPart.getJson();
            if (payload is json) {
                log:printInfo(payload.toJsonString());
            } else {
                log:printError(<string> payload.detail().message);
            }
        } else if (mime:TEXT_PLAIN == baseType) {
            var payload = bodyPart.getText();
            if (payload is string) {
                log:printInfo(payload);
            } else {
                log:printError(<string> payload.detail().message);
            }
        }
    }
}function getContentDispositionForFormData(string partName)
                                    returns (mime:ContentDisposition) {
    mime:ContentDisposition contentDisposition = new;
    contentDisposition.name = partName;
    contentDisposition.disposition = "form-data";
    return contentDisposition;
}

Request With Multiparts

Ballerina supports encoding and decoding multipart content in http requests along with nested parts. When you request multiparts from the HTTP inbound request, you get an array of body parts (an array of entities). You can loop through this array and handle the received body parts according to your requirement.

import ballerina/http;
import ballerina/log;
import ballerina/mime;
http:Client clientEP = new("http://localhost:9090");
@http:ServiceConfig {
    basePath: "/multiparts"
}
service multipartDemoService on new http:Listener(9090) {

Binds the listener to the service.

    @http:ResourceConfig {
        methods: ["POST"],
        path: "/decode"
    }
    resource function multipartReceiver(http:Caller caller, http:Request
                                        request) {
        http:Response response = new;
        var bodyParts = request.getBodyParts();
        if (bodyParts is mime:Entity[]) {
            foreach var part in bodyParts {
                handleContent(part);
            }
            response.setPayload(<@untainted> bodyParts);
        } else {
            log:printError(<string> bodyParts.reason());
            response.setPayload("Error in decoding multiparts!");
            response.statusCode = 500;
        }
        var result = caller->respond(response);
        if (result is error) {
            log:printError("Error sending response", err = result);
        }
    }

Extracts bodyparts from the request.

    @http:ResourceConfig {
        methods: ["GET"],
        path: "/encode"
    }
    resource function multipartSender(http:Caller caller, http:Request req) {
        mime:Entity jsonBodyPart = new;
        jsonBodyPart.setContentDisposition(
                        getContentDispositionForFormData("json part"));
        jsonBodyPart.setJson({"name": "wso2"});

Create a json body part.

        mime:Entity xmlFilePart = new;
        xmlFilePart.setContentDisposition(
                       getContentDispositionForFormData("xml file part"));

Create an xml body part as a file upload.

        xmlFilePart.setFileAsEntityBody("./files/test.xml",
                                        contentType = mime:APPLICATION_XML);

This file path is relative to where the ballerina is running. If your file is located outside, please give the absolute file path instead.

        mime:Entity[] bodyParts = [jsonBodyPart, xmlFilePart];
        http:Request request = new;

Create an array to hold all the body parts.

        request.setBodyParts(bodyParts, contentType = mime:MULTIPART_FORM_DATA);
        var returnResponse = clientEP->post("/multiparts/decode", request);
        if (returnResponse is http:Response) {
            var result = caller->respond(returnResponse);
            if (result is error) {
                log:printError("Error sending response", err = result);
            }
        } else {
            http:Response response = new;
            response.setPayload("Error occurred while sending multipart
                                    request!");
            response.statusCode = 500;
            var result = caller->respond(response);
            if (result is error) {
                log:printError("Error sending response", err = result);
            }
        }
    }
}

Set the body parts to the request. Here the content-type is set as multipart form data. This also works with any other multipart media type. eg:- multipart/mixed, multipart/related etc. You need to pass the content type that suit your requirement.

function handleContent(mime:Entity bodyPart) {
    var mediaType = mime:getMediaType(bodyPart.getContentType());
    if (mediaType is mime:MediaType) {
        string baseType = mediaType.getBaseType();
        if (mime:APPLICATION_XML == baseType || mime:TEXT_XML == baseType) {

The content logic that handles the body parts vary based on your requirement.

            var payload = bodyPart.getXml();
            if (payload is xml) {
                log:printInfo(payload.toString());
            } else {
                log:printError(<string> payload.detail().message);
            }
        } else if (mime:APPLICATION_JSON == baseType) {

Extracts xml data from the body part.

            var payload = bodyPart.getJson();
            if (payload is json) {
                log:printInfo(payload.toJsonString());
            } else {
                log:printError(<string> payload.detail().message);
            }
        } else if (mime:TEXT_PLAIN == baseType) {

Extracts json data from the body part.

            var payload = bodyPart.getText();
            if (payload is string) {
                log:printInfo(payload);
            } else {
                log:printError(<string> payload.detail().message);
            }
        }
    }
}

Extracts text data from the body part.

function getContentDispositionForFormData(string partName)
                                    returns (mime:ContentDisposition) {
    mime:ContentDisposition contentDisposition = new;
    contentDisposition.name = partName;
    contentDisposition.disposition = "form-data";
    return contentDisposition;
}
# To start the service, navigate to the directory that contains the
# `.bal` file and use the `ballerina run` command.
$ ballerina run request_with_multiparts.bal
[ballerina/http] started HTTP/WS listener 0.0.0.0:9090
# Start multipartDemoService
# The cURL command, which you need to execute to decode a multipart request
$ curl -F "part1={\"name\":\"ballerina\"};type=application/json" http://localhost:9090/multiparts/decode -H "Content-Type: multipart/mixed" -H 'Expect:'
--ac4875939cf6b158
content-type: application/json
content-disposition: form-data;name="part1"
content-id: 0
{"name":"ballerina"}
--ac4875939cf6b158--
# The cURL command, which you need to execute to encode the parts of the body and send a multipart request via the Ballerina service
$ curl -v http://localhost:9090/multiparts/encode
< HTTP/1.1 200 OK
< content-type: multipart/form-data; boundary=3bbfa10811dcdee6
< date: Thu, 14 Jun 2018 15:22:21 +0530
< server: ballerina/0.982.1-SNAPSHOT
< content-length: 398
<
--3bbfa10811dcdee6
content-type: application/json
content-disposition: form-data;name="json part"
content-id: 0
{"name":"wso2"}
--3bbfa10811dcdee6
content-type: application/xml
content-disposition: form-data;name="xml file part"
content-id: 1
<ballerinalang>
    <version>0.963</version>
    <test>test xml file to be used as a file part</test>
</ballerinalang>
--3bbfa10811dcdee6--