Azure-functions-host: Azure Functions v3 HttpTrigger: request body is empty

Created on 17 Dec 2019  路  21Comments  路  Source: Azure/azure-functions-host

HttpTrigger works incorrectly in the following conditions:

  • Azure Functions v3
  • TargetFramework: netcoreapp3.1
  • Microsoft.NET.Sdk.Functions version 3.0.2
  • Code:
        [FunctionName("RunNetCore3")]
        public static async Task<string> RunNetCore3Async(
            [HttpTrigger(AuthorizationLevel.Anonymous, "POST", Route = "netcore3/{id}")]
            CustomRouteParameter parameter,
            HttpRequest request,
            CancellationToken cancellationToken)
        {
            using (var reader = new StreamReader(request.Body, Encoding.UTF8))
            {
                return await reader.ReadToEndAsync();
            }
        }
  • The request body in the request variable is empty if request contains Content-Type: application/json header (works ok with any other Content-Type values except application/json). So in the example the empty response body will be returned which is unexpected.

Investigative information

I've created the repository to demonstrate this issue. The source code of this repository is deployed to the Function App.

  • Timestamp: 09:27 AM (UTC Time)
  • Function App version: 3.0
  • Function App name: NetCore3HttpBindingBug
  • Function name(s) (as appropriate): RunNetCore3
  • Invocation ID: 32894c31-74ac-4a98-8f94-fe6c76662d7d
  • Region: Central US

Repro steps

  • Send POST https://netcore3httpbindingbug.azurewebsites.net/api/netcore3/qqq request with some body and Content-Type: application/json header.

Expected behavior

Request's body is returned in the response.

Actual behavior

Request's body is not returned in the response.

Known workarounds

Do not send Content-Type: application/json header or send any other value in the Content-Type header.

Related information

The same code works correct in Azure Functions v2 on netcoreapp2.1 with Microsoft.NET.Sdk.Functions version 1.0.29 and in ASP.NET Core 3.1 Web Application.

There are two endpoints to check it (see the repository):

  • Send POST /netcore2/some-id request with some body and Content-Type: application/json header
  • Send POST /aspnetcore3/some-id request with some body and Content-Type: application/json header

In both cases the request body will be returned in the response (I didn't deploy it to any Function App but it's reproducible locally as well).

3.x P1 Supportability bug

Most helpful comment

Any word on this? Upgrading to v3 broke our microservices.

All 21 comments

Another workaround @fabiocav mentioned: set FUNCTIONS_V2_COMPATIBILITY_MODE to true in the App Settings.

Hello @pragnagopa, could you please confirm that it's a valid issue and if so, is it planned to be fixed on the Azure Functions side? Is there any ETA?

Hi. Working on a customer project and upgraded to version 3.1. I can verify that this is still an issue that needs to be resolved. Setting FUNCTIONS_V2_COMPATIBILITY_MODE to true works as a workaround though.

Please continue using FUNCTIONS_V2_COMPATIBILITY_MODE. We will update this issue with further details. No ETA yet.

Any word on this? Upgrading to v3 broke our microservices.

Having this issue and setting FUNCTIONS_V2_COMPATIBILITY_MODE does not help for me. Runs fine locally but nothing works when deployed to Azure. Am I missing something?

FUNCTIONS_V2_COMPATIBILITY_MODE is not doing the trick for me. You guys need to put a GIANT warning on the functions overview about this. I wasted so much time going in circles about this because I was not aware of this known issue. I am sure others have wasted time too. It is good to be more transparent about non obvious bugs like this.

Found another work around for others. In my case I was using GET on the function. If you use POST instead the body will come in correctly. I am not using the FUNCTIONS_V2_COMPATIBILITY_MODE.

Problem still exist when I am using:
Azure Functions Core Tools (3.0.2798 Commit hash: d9d99872a51c04b1c2783198b1ee1104da6b064f)

but after I tried it couple times, the issue suddenly disappear ... and get_json() returns json data ... this is really strange

if u are using postman:

  1. in body, header enter content-type: application/json
  2. in body type your json msg
  3. use POST and include a body

image
image

Any update on this issue? Setting FUNCTIONS_V2_COMPATIBILITY_MODE to true does not work. What is frustrating is that it works when invoked from the Test/Run form in the portal, but does not work via Postman.

I was able to repro this with @myarotskaya's repo. Looks like the problem has to do with model binding reading the body stream. By the time the function executes, there's nothing left to read. @fabiocav @pragnagopa Is this the desired behavior for v3? Feels like this is a bug if there's no other way to get access to the request body in this scenario.

@bstoked @TMUNYU @DanielTongAwesome @MateRadz Can you confirm that you are all experiencing the same issue? The fact that the compatibility mode doesn't solve it for everyone might indicate a different problem. Please share a repro if possible.

I do not knot your internal schedule but for me it is a new issue as my function without any change in code worked for several days until the last ok at 25.08.2020, 13:15:05.009. The next call at 25.08.2020, 19:16:20.619 (UTC) was the first not working an it is like this till today. So for me it may have been related to your internal updates, if there where any. As mentioned when called directly from Test/Run on Azure Portal it works.

Found another work around for others. In my case I was using GET on the function. If you use POST instead the body will come in correctly. I am not using the FUNCTIONS_V2_COMPATIBILITY_MODE.

That's not true for me at least; the first time I tripped over this issue it was with a POST message

I stumbled on this while investigating why the request body received from a (deployed to azure) function app is empty. Contrary to the above, I don't see this behaviour running locally. Even with content-type set to application/json I can successfully pass along a non-empty body.

PS C:\Users\me\source\project> func -v
3.0.2931

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <LangVersion>8.0</LangVersion>
    <Nullable>enable</Nullable>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
  </ItemGroup>
</Project>
namespace my.namespace
{
    public static class Notify
    {
        [FunctionName(nameof(Notify))]
        public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = "notify")] HttpRequest req,
        [Queue("name", Connection = "connName")] IAsyncCollector<string> myDestinationQueue,
        ILogger log)
        {
                using var r = new StreamReader(req.Body, Encoding.UTF8);
                var bodyStr = await r.ReadToEndAsync();
                await myDestinationQueue.AddAsync(bodyStr);
                // do stuff...
                return new OkObjectResult(1);
        }
    }
}

I do not knot your internal schedule but for me it is a new issue as my function without any change in code worked for several days until the last ok at 25.08.2020, 13:15:05.009. The next call at 25.08.2020, 19:16:20.619 (UTC) was the first not working an it is like this till today. So for me it may have been related to your internal updates, if there where any. As mentioned when called directly from Test/Run on Azure Portal it works.

The simplest workaround which worked for me was redeployment of function with MS provided example http trigger function.
After that worked I copied my source to the function and after deploying again it started to work.

I tried to reproduce the issue with a new func project however I couldn't

System

  • func --version -> 3.0.2931
  • dotnet --version -> 3.1.402

Steps

  • > func new ImageService --dotnet
  • > cd ImageService
  • > func init (Add a HTTP Trigger)
  • Swap code in the new HTTP Trigger class with this snippet
public static class CreatePDF
    {
        [FunctionName(nameof(CreatePDF))]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "createPDF")]
            HttpRequestMessage req,
            ILogger log)
        {
            var body = await req.Content.ReadAsStringAsync().ConfigureAwait(false);
            log.LogInformation(body);
            return new OkObjectResult(body);
        }
    }

    public static class MergePDF
    {
        [FunctionName(nameof(MergePDF))]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "mergePDF")]
            HttpRequest req,
            ILogger log)
        {
            var reader = new StreamReader(req.Body, Encoding.UTF8, true, 1024, true);
            {
                var body = await reader.ReadToEndAsync().ConfigureAwait(false);
                log.LogInformation(body);
                return new OkObjectResult(body);
            }
        }
    }
  • Now, invoke both the APIs and check for API response. (Both APIs return the request body for me)
    request_body_v3

@anthonychu The issue with @myarotskaya's git repo is the order of parameters
myarotskaya_issue

Once, the order of parameters is fixed, the request body comes through
myarotskaya_issue_fixed

I tried to reproduce the issue with a new func project however I couldn't

I can confirm this is now working for me with Microsoft.NET.Sdk.Functions 3.0.9, both in local emulator and in Azure itself, and with Get as well as Post.

Use Https instead of http
with the default setup of an http trigger azure function , that worked for me

Ultimately i had to switch to POST and use in function Model Binding, and NOT rely on parsing the request body. It seems no matter how hard I try, the body is often empty when the azure function is 'waking up' (first hit on a cold function), but on subsequent hits it is valid and works.

FUNCTIONS_V2_COMPATIBILITY_MODE to true didn't do me any good.

This is my new start:

[FunctionName("GetComics")]
        [FixedDelayRetry(5, "00:00:02")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]
            GetComicsRequest Request,
            ILogger log
            )
        {
        ...
        var Response = new ComicResponse()
                {
                    Date = (Comics.Count > 0 ? Comics[0].Date : Request.Date != DateTimeHelper.ZERO_TIME ? Request.Date : DateTime.Now),
                    Comics = Comics
                };
                return new JsonResult(Response);
        }
Was this page helpful?
0 / 5 - 0 ratings