.NET 8 Azure Functions: setting System.Text.JsonSerializer defaults

294 Views Asked by At

I'm trying to set a convertor to be used on every request to an Azure Function using a JsonResult.

I'm using the new VS2022 templates for azure functions using .NET 8. This means: the extension ConfigureFunctionsWebApplication is used and not the older ConfigureFunctionWorker.

I'm trying to set the options as it seems to be used in https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/Infrastructure/SystemTextJsonResultExecutor.cs#L26

but it isn't working.

.ConfigureFunctionsWebApplication((hostBuilderContext, functionsWorkerApplicationBuilder) =>
    {
        functionsWorkerApplicationBuilder.Services.Configure<JsonOptions>(options =>
        {
            options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
            options.SerializerOptions.PropertyNameCaseInsensitive = true;
            #if DEBUG
            options.SerializerOptions.WriteIndented = true;
            #endif
        });
    }

does absolutely nothing.

I can get it working if I replace it with a call like:

functionsWorkerApplicationBuilder.Services.AddMvcCore().AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

But that might drag in a lot of stuff I don't want or need.

Does anyone know the proper way to set the jsonserialization defaults?

And please don't answer with answers that are valid for older .NET version or Newtonsoft. Especially newtonsoft seems to be on the way out of the framework.

edit:

I appreciate people looking over my question, but I explicitly asked for an answer pertaining to the latest way of building Azure Functions as advertised by Microsoft. https://learn.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide?tabs=windows#http-trigger So not the build in classes HttpRequestData and HttpResponseData, but the classes OkObjectResult en JsonResult that come from the new mvc core integration. These seem to be the current way forward.

2

There are 2 best solutions below

2
zu1b On BEST ANSWER

I finally figured out the problem. There is Microsoft.AspNetCore.Http.Json.JsonOptions and there is Microsoft.AspNetCore.Mvc.JsonOptions.

JsonResult which works with IActionResultExecutor uses Microsoft.AspNetCore.Mvc.JsonOptions.

So this will work:

        services.Configure<Microsoft.AspNetCore.Mvc.JsonOptions>(options =>
        {
            options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
            options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
        });

But this won't:

        services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
        {
            options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
            options.SerializerOptions.PropertyNameCaseInsensitive = true;
       });

This cost me hours. Wish microsoft didn't reuse classnames so blatently

10
Andrew B On

Does this help?

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(worker => { })
    .ConfigureServices(services =>
    {
        services
            .AddHttpClient() // etc
            .Configure<JsonSerializerOptions>(options =>
            {
                // Whatever options
                options.AllowTrailingCommas = true;
                options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
                options.PropertyNameCaseInsensitive = true;
                options.IncludeFields = true;
            })

And then the default Json methods automatically use that config, e.g. when using HttpRequestData and HttpResponseData

[Function(nameof(SomePostMethod))]
public Task<HttpResponseData> SomePostMethod([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "my-api/route")] HttpRequestData req)
{
    var received = await req.ReadFromJsonAsync<MyType>(); // Automatically uses your Json options.

    var result = DoSomethingWithReceivedData(received);

    var response = req.CreateResponse(HttpStatusCode.OK);
    await response.WriteAsJsonAsync(result); // Automatically uses your Json options.
    return response;
};

This is from a .NET 8 Isolated Functions application that I'm developing. Definitely works for me.