Search code examples
asp.net-coreaws-lambdahttpcontext

Unable to read request body from HttpContext on asp.net core api deployed on Lambda


I have an asp.net core wed api which is currently hosted on IIS and I am trying to migrate it to AWS Lambda.

For this api I have written a logging middleware which logs all incoming requests and their respective responses. The code for the InvokeAsync method of the middleware is the following (only the relevant parts)

public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            var request = context.Request;

            var requestTime = DateTime.UtcNow;
            var requestBodyContent = await ReadRequestBody(request);
            var originalBodyStream = context.Response.Body;
            using (var responseBody = new MemoryStream())
            {
                var response = context.Response;
                //The context.Response.Body memory stream cannot be read from more than once. If we read it here to log it the response to the client will be empty 
                //Instead we have all subsequent middleware write to the temporary responseBody memory stream which we can later read from
                response.Body = responseBody;

                //calculate the duration of the call
                Stopwatch callDurationTimer = new Stopwatch();
                callDurationTimer.Start();
                await next(context);
                callDurationTimer.Stop();

                string responseBodyContent = null;
                responseBodyContent = await ReadResponseBody(response);

                //we write the contents of the temporary response memory stream to the original response memory stream in order to return the correct response. 
                await responseBody.CopyToAsync(originalBodyStream);
                /* 
                   code that logs to a DB
                */
            }
        }
        catch (Exception e)
        {
            logger.LogError(e, "Logging Middleware Error");
            await next(context);
        }
    }

and the code for the ReadRequestBody function

 private async Task<string> ReadRequestBody(HttpRequest request)
    {
        request.EnableRewind();            

        var buffer = new byte[Convert.ToInt32(request.ContentLength)];
        await request.Body.ReadAsync(buffer, 0, buffer.Length);
        var bodyAsText = Encoding.UTF8.GetString(buffer);
        request.Body.Seek(0, SeekOrigin.Begin);

        bodyAsText = Regex.Replace(bodyAsText, @"\s+", "");
        return bodyAsText;
    }

The above code works as expected in the IIS hosted scenario.

In the Lambda scenario I have the following problem: Trying to read the request body yields nothing. The result of the ReadRequestBodyFunction is an empty string. Everything else works normally. Both logging and the rest of the system.

Does anybody know what Lambda does with the request before handing it over to my api that makes the request body unreadable?

If it is important at all I am using asp.net core 2.2 and the app is self contained so that it can run on Lambda which only supports .net core 2.1 natively.


Solution

  • I found the solution by looking at the Amazon.Lambda.AspNetCoreServer package code (v4.0.0). At some point when they marshal the request they set the body of the request to a MemoryStream. This seems to somehow mess with the request.ContentLength Property which in this line of code was always null var buffer = new byte[Convert.ToInt32(request.ContentLength)];

    The request.Body.Length property was working as expected though. So changing the above line to var buffer = new byte[Convert.ToInt32(request.Body.Length)];

    solved the problem.