首页 > 解决方案 > 无法替换 ASP.Core 3 中的默认 JSON 合同解析器

问题描述

在基于 .NET Core 3.0 框架创建基本的 Web API 项目后,所有 API 响应都以驼峰形式出现。我从 System.Text.Json 安装了 SwashBuckle Swagger + 内置 JSON 序列化程序,特别是将枚举显示为字符串,一切都像以前一样工作。然后,我决定切换到 NSwag + NewtonSoftJson,因为带有动态和扩展对象的内置序列化程序的一些限制。现在,所有 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
  }
}

启动.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");
}

标签: asp.net-coreasp.net-core-3.0

解决方案


仍然不明白为什么自定义合约解析器不是由 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()
};

想法取自这篇文章

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


推荐阅读