Friday, October 29, 2021

Azure Functions Binary Data Transfer via OpenAPI

Since the latest version (0.9.0-preview) of the OpenAPI extension for Azure Functions was released, it supports the byte array types. With this support, you can now define the binary data type like image files onto the OpenAPI document. Throughout this post, I'm going to discuss how to declare the binary data and transfer it through the Azure Functions OpenAPI extension.

You can download the sample app code from this GitHub repository.

Binary Data Transfer with text/plain Content Type

Let's look at the function below. It directly passes the based64 encoded string to Azure Function app through the request payload. It would be best to assume that the binary data has already been converted to a base64 string. The function defines the content type of text/plain and data type of byte[] (line #7). For the response payload, it defines the content type of image/png and data type of byte[] (line #9). As the actual data you are passing is the base64 encoded string, all you need to do is to convert the encoded string into byte array (line #15-21).

 

public static class BinaryDataHttpTrigger
{
    [FunctionName(nameof(BinaryDataHttpTrigger.RunByteArray))]
    [OpenApiOperation(operationId: "run.bytearray", tags: new[] { "bytearray" }, ...)]
    [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]

    [OpenApiRequestBody(contentType: "text/plain", bodyType: typeof(byte[]), ...)]

    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), ...)]

    public static async Task<IActionResult> RunByteArray(
        [HttpTrigger(AuthorizationLevel.Function, "POST", Route = "bytearray")] HttpRequest req,
        ILogger log)
    {
        var payload = default(string);
        using (var reader = new StreamReader(req.Body))
        {
            payload = await reader.ReadToEndAsync().ConfigureAwait(false);
        }

        var content = Convert.FromBase64String(payload);

        var result = new FileContentResult(content, "image/png");

        return result;
    }
}

 

The OpenAPI document about this part looks like below (omitted unrelated lines for brevity). First, the request payload is defined as text/plain, with the data type of string and format of binary (line #7-10). Next, the response payload is also defined as image/png, with the data type of string and format of binary (line #16-19).

 

paths:
  /bytearray:
    post:
      requestBody:

        content:
          text/plain:
            schema:
              type: string
              format: base64

      responses:
        '200':

          content:
            image/png:
              schema:
                type: string
                format: base64

 

Once you run the function app on your local machine, it looks like below. The image data is transferred correctly.

Byte Array

Binary Data Transfer with multipart/form-data Content Type

Let's transfer both binary data and text data through the multipart/form-data content type. This content type is the most typical way to transfer binary data through API. The request payload declares the content type of multipart/form-data and data type of MultiPartFormDataModel (line #7). MultiPartFormDataModel is a DTO that contains the Image property with the byte array type (line #34). When you send a POST request to the API endpoint, because you use the multipart/form-data content type, the image data should be retrieved from req.Form.Files[0], and you need to convert it to the byte array (line #15-23).

 

public static class BinaryDataHttpTrigger
{
    [FunctionName(nameof(BinaryDataHttpTrigger.RunMultiPartFormData))]
    [OpenApiOperation(operationId: "run.multipart.formdata", tags: new[] { "multipartformdata" }, ...)]
    [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]

    [OpenApiRequestBody(contentType: "multipart/form-data", bodyType: typeof(MultiPartFormDataModel), ...)]

    [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "image/png", bodyType: typeof(byte[]), ...)]

    public static async Task<IActionResult> RunMultiPartFormData(
        [HttpTrigger(AuthorizationLevel.Function, "POST", Route = "form/multipart")] HttpRequest req,
        ILogger log)
    {
        var files = req.Form.Files;
        var file = files[0];

        var content = default(byte[]);
        using (var ms = new MemoryStream())
        {
            await file.CopyToAsync(ms).ConfigureAwait(false);
            content = ms.ToArray();
        }

        var result = new FileContentResult(content, "image/png");

        return result;
    }
}

public class MultiPartFormDataModel
{
    public int Id { get; set; }
    public string Description { get; set; }
    public byte[] Image { get; set; }
}

 

The OpenAPI document related to this part looks like below (omitted unrelated lines for brevity). The request payload has a reference to the object (line #9), and the object has a property of image, with the data type of string and format of binary (line #31-33).

 

paths:
  /form/multipart:
    post:
      requestBody:

        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/multiPartFormDataModel'

      responses:
        '200':

          content:
            image/png:
              schema:
                type: string
                format: base64

components:
  schemas:
    multiPartFormDataModel:
      type: object
      properties:
        id:
          type: integer
          format: int32
        description:
          type: string

        image:
          type: string
          format: base64

 

Run the function app and see how it's going. Your image data has been transferred successfully through the multipart/form-data content type.

Multi-Part Form-Data


So far, we've walked through how to define binary data through Azure Functions OpenAPI extension and run it on Swagger UI. Since this feature was one of the long-waited ones, I'm hoping everyone can make use of this feature in many places.

This article was originally published on Dev Kimchi.

Posted at https://bit.ly/3nKGgkX