无法替换 ASP.Core 中的默认 JSON 合同解析器 3

Cannot replace default JSON contract resolver in ASP.Core 3

在基于 .NET Core 3.0 框架创建基本 Web API 项目后,所有 API 响应均采用驼峰式。我安装了 SwashBuckle Swagger + 来自 System.Text.Json 的内置 JSON 序列化程序,特别是将枚举显示为字符串,一切都像以前一样工作。然后,我决定切换到 NSwag + NewtonSoftJson,因为内置序列化器对动态和 expando 对象有一些限制。现在,所有 API 响应都显示在 PascalCase 中,我既不能更改命名策略,也不能创建自定义合同解析器。

例子

https://forums.asp.net/t/2138758.aspx?Configure+SerializerSettings+ContractResolver

问题

我怀疑可能某些包在幕后覆盖了合同解析器。如何确保 API 服务仅使用我在启动时分配的自定义合同解析器并忽略所有其他类似设置?

自定义 JSON 合约解析器

public class CustomContractResolver : IContractResolver
{
  private readonly IHttpContextAccessor _context;
  private readonly IContractResolver _contract;
  private readonly IContractResolver _camelCaseContract;

  public CustomContractResolver(IHttpContextAccessor context)
  {
    _context = context;
    _contract = new DefaultContractResolver();
    _camelCaseContract = new CamelCasePropertyNamesContractResolver();
  }

  // When API endpoint is hit, this method is NOT triggered

  public JsonContract ResolveContract(Type value)
  {
    return _camelCaseContract.ResolveContract(value);
  }
}

控制器

[ApiController]
public class RecordsController : ControllerBase
{
  [HttpGet]
  [Route("services/records")]
  [ProducesResponseType(typeof(ResponseModel<RecordEntity>), 200)]
  public async Task<IActionResult> Records([FromQuery] QueryModel queryModel)
  {
    var response = new ResponseModel<RecordEntity>();

    return Content(JsonConvert.SerializeObject(response), "application/json"); // NewtonSoft serializer
  }
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
  services
    .AddCors(o => o.AddDefaultPolicy(builder => builder
      .AllowAnyOrigin()
      .AllowAnyHeader()
      .AllowAnyMethod()));

  services
    .AddControllers(o => o.RespectBrowserAcceptHeader = true)
    /*
    .AddJsonOptions(o =>
    {
      o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
      o.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
      o.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    })
    */
    .AddNewtonsoftJson(o =>
    {
      o.UseCamelCasing(true);
      o.SerializerSettings.Converters.Add(new StringEnumConverter());
      //o.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver { NamingStrategy = new CamelCaseNamingStrategy() };
      o.SerializerSettings.ContractResolver = new CustomContractResolver(new HttpContextAccessor());
    });

  services.AddOpenApiDocument(o =>   // NSwag
  {
    o.PostProcess = document =>
    {
      document.Info.Version = "v1";
      document.Info.Title = "Demo API";
    };
  });

  DataConnection.DefaultSettings = new ConnectionManager(DatabaseOptionManager.Instance); // LINQ to DB
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
  }

  app.UseCors(o => o.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
  app.UseRouting();
  app.UseAuthorization();
  app.UseEndpoints(o => o.MapControllers());

  app.UseOpenApi();                                // NSwag
  app.UseSwaggerUi3(o => o.Path = "/v2/docs");
  app.UseReDoc(o => o.Path = "/v1/docs");
}

仍然不明白为什么 API 端点不触发自定义合同解析器,但找到了一个适合我将 API 切换为驼峰式大小写的组合。随意解释为什么它以这种方式工作。

services.AddControllers(o => o.RespectBrowserAcceptHeader = true)

  // Options for System.Text.Json don't affect anything, can be uncommented or removed

  //.AddJsonOptions(o =>
  //{
  //  o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
  //  o.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
  //  o.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
  //})

  .AddNewtonsoftJson(o =>
  {
    o.UseCamelCasing(true);
    o.SerializerSettings.Converters.Add(new StringEnumConverter());

    // This option below breaks global settings, so had to comment it

    //o.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver 
    //{
    //  NamingStrategy = new CamelCaseNamingStrategy()
    //};
  });

JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
  ContractResolver = new CamelCasePropertyNamesContractResolver()
};

灵感来源于此article

NewtonSoft 允许设置全局序列化设置,忽略 MVC、Web API 和其他框架。