Multipart data with Azure Functions HttpTriggers

When it comes to making Azure Functions that accepts HTTP requests using HttpTriggers it's by default fairly easy to read data being posted. But what if we want to post multipart data where we mix information and images in a single HTTP request, let's find out.

If you have a background using ASPNET WebAPI you're properly aware of the [FromBody] or [FromForm] attributes you can use and magic will happen with modelbinding. Unfortunately we are not there yet with Azure Functions and we need to do a little more work to consume the data. Let's assume we have an endpoint with a HttpTrigger, where we can perform a POST to create a profile. The data will be information describing the profile together with a profile image, all in a single request. Using POSTMAN this could look like this:

Posting formdata

It's important to note the body data is added as form-data. Name, email and interests are all added as TEXT and File is added as FILE. In POSTMAN you can change the type if you hover over the key field. Also note interests are added as an array, we'll see how we read this later. If we look at the request by clicking the Code button in POSTMAN, we can see the requests content-type is multipart/form-data

Let's code

Start by creating a HttpTriggered Azure Function. When the function is created, we change the input to only accept POST and then we ensure that refactoring is easier supported by using nameof to name the function.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace CreateProfile
{
    public static class CreateProfileFunctions
    {
        [FunctionName(nameof(CreateProfile))]
        public static async Task<IActionResult> CreateProfile(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "v1/profile")] HttpRequest req,
            ILogger log)
        {

            var formdata = await req.ReadFormAsync();

            string name = formdata["name"];
            string[] interests = JsonConvert.DeserializeObject<string[]>( formdata["interests"]);

            var image = req.Form.Files["file"];

            // do stuff with data.....

            return name != null
                ? (ActionResult)new OkObjectResult($"Thank you")
                : new BadRequestObjectResult("Sorry didn't get it all....");
        }
    }
}

So what's going on here? First I've change the route of the trigger to v1/profile, this will result in a final endpoint as http://localhost:7071/api/v1/profile if you're running local.

Next up is reading TEXT data from the request. This can be either async or sync, I used ReadFormAsync in this example. After reading the formdata, we can simply access it by its key(s), we defined in the request or by its index. In a real world scenario you should make some checking, which is also supported.  Reading a field can be done like this.

string name = formdata["name"];

The "interests" data can be read like this, using Newtonsoft.Json

string[] interests = JsonConvert.DeserializeObject<string[]>( formdata["interests"]);

Finally we need to read the image data. As the request is multipart, we got both the image and data in a single request. This means we can now access the image from the Form using its Files property.

var image = req.Form.Files["file"];

Once again I'm using the key name file from the request to access the image. Once the image is read we can see all its details.

Image file details

That's all there is to reading multipart data with an Azure Function.