0

I have scaffolded an Azure Functions application using .NET 9. The Program.cs file looks like this:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = FunctionsApplication.CreateBuilder(args);

builder.ConfigureFunctionsWebApplication();

builder.Services
    .AddApplicationInsightsTelemetryWorkerService()
    .ConfigureFunctionsApplicationInsights();

builder.Build().Run();

Inspecting the code reveals ConfigureFunctionsWebApplication calls Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions.AddFunctionsWorkerDefaults which configures default JSON serialisation to ignore property name case.

This is not the behaviour I am experiencing. My endpoint is configured to accept the following JSON body:

{
  "Double": 2
}

If the parameter name is lowercase, the JSON deserialization fails and the value is defaulted to zero.

I found this post suggesting either Microsoft.AspNetCore.Http.Json.JsonOptions or Microsoft.AspNetCore.Mvc.JsonOptions needs to be configured. I don't believe this should be necessary and neither option worked for me anyway. .NET 8 Azure Functions: setting System.Text.JsonSerializer defaults

Any suggestions? I have committed sample code here https://github.com/DangerousDarlow/AzureFunctionsExplore

The function code is https://github.com/DangerousDarlow/AzureFunctionsExplore/blob/master/AzureFunctionsExplore/Math.cs

public class Math(ILogger<Math> logger)
{
    [Function("Double")]
    [OpenApiOperation("Double")]
    [OpenApiRequestBody("application/json", typeof(Body), Required = true)]
    [OpenApiResponseWithBody(HttpStatusCode.OK, "'application/json'", typeof(Body))]
    public async Task<IActionResult> Double([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest request)
    {
        var requestBodyString = await new StreamReader(request.Body).ReadToEndAsync();
        var requestBody = JsonSerializer.Deserialize<Body>(requestBodyString);
        logger.LogInformation("Doubling number: {number}", requestBody?.Double);
        return new OkObjectResult(new Body (requestBody?.Double * 2 ?? 0 ));
    }

    public record Body(int Double);
}
3
  • Add this code snippet in your function code var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; var requestBody = JsonSerializer.Deserialize<Body>(requestBodyString, options); to ignore case during deserialization. It worked for me. Commented Jun 10 at 8:38
  • Thank you. I will give that a try. But it shouldn't be necessary surely as ConfigureFunctionsWebApplication should configure JSON serialization to be parameter name case insensitive? Also I'd prefer to configure this application wide rather than adding it explicitly to each function. Commented Jun 10 at 8:41
  • Hmm. Perhaps the problem is I am not using the application JSON serializer rather a different static instance? Commented Jun 10 at 8:43

2 Answers 2

0

By default, System.Text.Json is case-sensitive when deserializing unless explicitly told otherwise. When the JSON uses "double", it doesn't match with Double property in Body record, so it doesn't get deserialized and responds with value0.

To change this, the only workaround I could come up with is configuring JsonSerializerOptions.PropertyNameCaseInsensitive to true in function code as I mentioned in the comments.

  var options = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };

Function.cs:

public class Math(ILogger<Math> logger)
{
    [Function("Double")]
    [OpenApiOperation("Double")]
    [OpenApiRequestBody("application/json", typeof(Body), Required = true)]
    [OpenApiResponseWithBody(HttpStatusCode.OK, "'application/json'", typeof(Body))]
    public async Task<IActionResult> Double([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest request)
    {
        var requestBodyString = await new StreamReader(request.Body).ReadToEndAsync();
        var options = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };

        var requestBody = JsonSerializer.Deserialize<Body>(requestBodyString, options);
        
        logger.LogInformation("Doubling number: {number}", requestBody?.Double);
        return new OkObjectResult(new Body (requestBody?.Double * 2 ?? 0 ));
    }

    public record Body(int Double);
}

Output:

enter image description here

Sign up to request clarification or add additional context in comments.

1 Comment

0

After further investigation I have concluded that function ConfigureFunctionsWebApplication does initialise JSON serialization with sensible options including case insensitive parameter names however I was not using these options with the serializer in my endpoint implementation. I needed to inject them into the class implementing the endpoint and pass them to the serializer.

public class Math(IOptions<JsonSerializerOptions> jsonSerializerOptions, ILogger<Math> logger)
{
    private readonly JsonSerializerOptions _jsonSerializerOptions = jsonSerializerOptions.Value;
    
    [Function("Double")]
    [OpenApiOperation("Double")]
    [OpenApiRequestBody("application/json", typeof(Body), Required = true)]
    [OpenApiResponseWithBody(HttpStatusCode.OK, "'application/json'", typeof(Body))]
    public async Task<IActionResult> Double([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest request)
    {
        var requestBodyString = await new StreamReader(request.Body).ReadToEndAsync();
        var requestBody = JsonSerializer.Deserialize<Body>(requestBodyString, _jsonSerializerOptions);
        logger.LogInformation("Doubling number: {number}", requestBody?.Double);
        return new OkObjectResult(new Body (requestBody?.Double * 2 ?? 0 ));
    }

    public record Body(int Double);
}

It may also be possible to configure the default options used by the static JsonSerializer instance however I haven't explored this further as injecting the options suits my purpose and it's clear.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.