Azure Function Middleware: Request and Response Body Retrieval

Azure Function Middleware: Request and Response Body Retrieval

The other day, I encountered a use case where I needed to implement middleware in my HTTP Trigger Azure Function to validate input from an HTTP request before executing my main function. Additionally, before returning the function output to the requester, I intended to perform last-minute output encoding to remove any malicious script content.

To accomplish this, I need to create middleware that parses the content of the request and modifies the response body.

using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Middleware;
using System.Text;
using System.Text.Encodings.Web;

namespace DataSanitization.middleware
{
    internal sealed class MyCustomMiddleware: IFunctionsWorkerMiddleware
    {
        private readonly FunctionExecutionDelegate _next;
        private readonly HtmlEncoder _htmlEncoder;

        public MyCustomMiddleware(FunctionExecutionDelegate next, HtmlEncoder htmlEncoder)
        {
            _next = next;
            _htmlEncoder = htmlEncoder;
        }

        public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
        {
            //Call the next middleware or function in the pipeline
            await _next(context);
        }
    }
}

If you are already familiar with middleware, any code placed before the await _next(context) line will be executed before your Azure function, while anything after that line will be executed after your function.

With that in mind, before our function executes, we aim to read the HTTP request body and validate its content.

var httpRequestData = await context.GetHttpRequestDataAsync();

if (httpRequestData != null)
{
    using var inputReader = new StreamReader(httpRequestData.Body, Encoding.UTF8, false, 1024, leaveOpen: true);
    var inputBody = await inputReader.ReadToEndAsync();

    //logics to validate the input body here

   // this is required, otherwise model binding will return null
    httpRequestData.Body.Seek(0, SeekOrigin.Begin);
}

Once you have the inputBody string, you can apply any validation method to ensure your input data meets your standards. Don't forget to call httpRequestData.Body.Seek(0, SeekOrigin.Begin) to properly reset the stream pointer, allowing the data to pass through the pipeline.

Once our main Azure function executes and produces output to send back to the requester, we aim to capture that content for sanitization purposes by encoding specific portions of it to ensure safety.

var httpResponseData = context.GetHttpResponseData();

// Stream pointer was ending up at the end of stream. need to reset the pointer before reading the content of stream.
// Otherwise the stream will always results in empty string when reading it.
httpResponseData!.Body.Seek(0, SeekOrigin.Begin); 

using var outputReader = new StreamReader(httpResponseData.Body, Encoding.UTF8, false, 1024, leaveOpen: true);
var outputBody = await outputReader.ReadToEndAsync();

//Logics to santize the outputBody before sending it back to the requester