1

I want to add global JsonSerializer options to use ReferenceHandler.Preserve, i can't configure my blazor server App to use it as a global setting for all json Serializers.

i used

builder.Services.ConfigureHttpJsonOptions(options =>
    {
        options.SerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
        options.SerializerOptions.PropertyNameCaseInsensitive = true;
    });

builder.Services.AddRazorPages().AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
        options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
    });

builder.Services.Configure<JsonOptions>(o =>
   {
       o.SerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
       o.SerializerOptions.PropertyNameCaseInsensitive = true;
   });

none of them works as expected the options doesn't change from the defaults and i keep getting the same exception: "The JSON value could not be converted to" using the same options at each request works

var options = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve, PropertyNameCaseInsensitive = true };
var httpClient = _httpFactory.CreateClient("API");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _tokenProvider.JwtToken);
var result = await httpClient.GetFromJsonAsync<List<Manufacturer>>("manufacturer", options);

but i want to define the options for all requests without explicitly writing them each time.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
Ahmed Saad
  • 23
  • 6
  • 3
    Many apps provide default `JsonSerializerOptions` via some builder, but System.Text.Json does not have global default options that are modifiable. See [this answer](https://stackoverflow.com/a/58331570/3744182) by Chris Yungmann to [How to globally set default options for System.Text.Json.JsonSerializer?](https://stackoverflow.com/q/58331479/3744182). Your best option would be to add some extension method to `HttpClient` that encapsulates your desired global options as shown in [this answer](https://stackoverflow.com/a/58331912/3744182) by ps2goat. – dbc Jan 12 '23 at 16:43

1 Answers1

0

Instead of Named Client you could use Typed Client (or even generated with NSwag or Refit) and handle JSON formatting options inside this typed API client.

E.g. NSwag API clients generator has an option to generate UpdateJsonSerializerSettings method which you can define in the base class for type dAPI client like:

internal class BaseClient
{
    protected static void UpdateJsonSerializerSettings(JsonSerializerOptions settings) => settings.ConfigureDefaults();
}

// generated API client: 
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.16.1.0 (NJsonSchema v10.7.2.0 (Newtonsoft.Json v13.0.0.0))")]
internal partial class SomeClient : BaseClient, ISomeClient
{
    private System.Net.Http.HttpClient _httpClient;
    private System.Lazy<System.Text.Json.JsonSerializerOptions> _settings;

    public ActionsClient(System.Net.Http.HttpClient httpClient)
    {
        _httpClient = httpClient;
        _settings = new System.Lazy<System.Text.Json.JsonSerializerOptions>(CreateSerializerSettings);
    }

    private System.Text.Json.JsonSerializerOptions CreateSerializerSettings()
    {
        var settings = new System.Text.Json.JsonSerializerOptions();
        UpdateJsonSerializerSettings(settings);
        return settings;
    }
    ... the rest of generated code has omitted 
}

// Extension which configures the default JSON settings (as an example): 
public static class JsonExtensions
{
    private static JsonSerializerOptions GetDefaultOptions() => new() { WriteIndented = true };

    public static JsonSerializerOptions ConfigureDefaults(this JsonSerializerOptions? settings)
    {
        settings ??= GetDefaultOptions();

        settings.PropertyNameCaseInsensitive = true;
        settings.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        settings.NumberHandling = JsonNumberHandling.AllowReadingFromString;
        settings.Converters.Add(new JsonStringEnumConverter());

        return settings;
    }
}

Then you register on DI your ISomeClient typed client like this:

builder.Services
    .AddScoped<MyLovelyAuthorizationMessageHandler>()
    .AddHttpClient<ISomeClient, SomeClient>(httpClient => 
    {
        httpClient.BaseAddress = new Uri(apiSettings.WebApiBaseAddress);
        // ...etc.
    }).AddHttpMessageHandler<MyLovelyAuthorizationMessageHandler>();

And then - inject ISomeClient where you need it, and call methods with typed DTOs keeping all JSON serialization/deserialization magic under the carpet.

[Inject] private ISomeClient Client {get; set;} = default!;

Documentation: about Typed Clients

Dmitry Pavlov
  • 30,789
  • 8
  • 97
  • 121