With ASP.NET Core MVC, it's possible to return a result by multiple ways.
Controller action return types in ASP.NET Core web API :
IActionResult
HttpResult
But when the controller action returns HttpResult
, ASP.NET Core MVC serialization options are ignored.
Example
Program.cs
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers().AddJsonOptions(
o => o.JsonSerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.SnakeCaseUpper
);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.MapControllers();
app.Run();
FoosController.cs
:
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
namespace WebApplication.Controllers;
[Route("foos")]
public class FoosController
{
[HttpGet("raw")]
public Foo Raw() => new Foo();
[HttpGet("action-result")]
public IActionResult Action() => new OkObjectResult(new Foo());
[HttpGet("http-result")]
public IResult Http() => Results.Ok(new Foo());
[HttpGet("typed-result")]
public Ok<Foo> Typed() => TypedResults.Ok(new Foo());
}
public class Foo
{
public int Id { get; set; }
public string Label { get; set; } = "";
}
The endpoints /raw
and /action-result
return :
{
"ID": 0,
"LABEL": ""
}
But the endpoints /http-result
and /typed-result
return :
{
"id": 0,
"label": ""
}
How can I specify serialization options for HttpResult
?
MCV has Microsoft.AspNetCore.Mvc.JsonOptions
to configure serialization options, HttpResult has Microsoft.AspNetCore.Http.Json.JsonOptions
.
You can configure serialization options for HttpResult like :
builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(
o => o.SerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.SnakeCaseUpper
);
Trick to mutualise the configuration of serialization options between MVC and HttpResult :
builder.Services.AddControllers().AddJsonOptions(
o => o.JsonSerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.SnakeCaseUpper
);
builder.Services.AddSingleton(p =>
{
var mOpt = p.GetRequiredService<IOptions<Microsoft.AspNetCore.Mvc.JsonOptions>>().Value;
var hOpt = new Microsoft.AspNetCore.Http.Json.JsonOptions();
hOpt.SerializerOptions.AllowTrailingCommas = mOpt.JsonSerializerOptions.AllowTrailingCommas;
hOpt.SerializerOptions.DefaultBufferSize = mOpt.JsonSerializerOptions.DefaultBufferSize;
hOpt.SerializerOptions.NumberHandling = mOpt.JsonSerializerOptions.NumberHandling;
hOpt.SerializerOptions.PreferredObjectCreationHandling = mOpt.JsonSerializerOptions.PreferredObjectCreationHandling;
hOpt.SerializerOptions.PropertyNameCaseInsensitive = mOpt.JsonSerializerOptions.PropertyNameCaseInsensitive;
hOpt.SerializerOptions.PropertyNamingPolicy = mOpt.JsonSerializerOptions.PropertyNamingPolicy;
hOpt.SerializerOptions.ReadCommentHandling = mOpt.JsonSerializerOptions.ReadCommentHandling;
hOpt.SerializerOptions.ReferenceHandler = mOpt.JsonSerializerOptions.ReferenceHandler;
hOpt.SerializerOptions.TypeInfoResolver = mOpt.JsonSerializerOptions.TypeInfoResolver;
hOpt.SerializerOptions.UnknownTypeHandling = mOpt.JsonSerializerOptions.UnknownTypeHandling;
hOpt.SerializerOptions.UnmappedMemberHandling = mOpt.JsonSerializerOptions.UnmappedMemberHandling;
hOpt.SerializerOptions.WriteIndented = mOpt.JsonSerializerOptions.WriteIndented;
hOpt.SerializerOptions.TypeInfoResolverChain.Clear();
mOpt.JsonSerializerOptions.TypeInfoResolverChain.ToList().ForEach(r => hOpt.SerializerOptions.TypeInfoResolverChain.Add(r));
hOpt.SerializerOptions.Converters.Clear();
mOpt.JsonSerializerOptions.Converters.ToList().ForEach(c => hOpt.SerializerOptions.Converters.Add(c));
return Options.Create(hOpt);
});
Have to think about adapting it to each evolution of JsonSerializerOptions
.