You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8 Minimal API中Newtonsoft自定义转换器未触发问题排查

问题:.NET 8 Minimal API中System.Version自定义序列化不生效

我们正在将版本检查API从.NET Core 3.1升级至.NET 8 Minimal API,需保证向后兼容。自.NET 6起,System.Version的默认JSON序列化方式变更为字符串格式(如"9.16.2"),但我们需要保持旧的对象格式输出:

{"version":{"major":9,"minor":16,"build":6,"revision":-1,"majorRevision":-1,"minorRevision":-1}}

先后尝试了Newtonsoft.Json和System.Text.Json的自定义转换器,但调用/version端点时始终使用默认序列化逻辑,无法触发自定义逻辑。

核心原因

  1. Minimal API与Controllers配置隔离:Minimal API的Results.Ok等返回方法默认使用全局System.Text.Json配置,而非Controllers中单独配置的序列化规则(不管是Newtonsoft还是System.Text.Json)。
  2. 重复注册Controllers:Program.cs中重复调用builder.Services.AddControllers(),覆盖了之前添加的序列化配置。

解决方案

方案1:统一使用Newtonsoft.Json处理所有序列化

步骤1:修复Program.cs中的重复注册问题

删除重复的builder.Services.AddControllers();,确保只保留一次带Newtonsoft配置的注册:

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

// 只保留这一次AddControllers注册,添加Newtonsoft配置
builder.Services.AddControllers().AddNewtonsoftJson(options =>
{
    options.SerializerSettings.Converters.Add(new VersionConverter());
});

// 其他服务注册...
builder.Services.AddApiVersioning(options =>
{
    options.ReportApiVersions = true;
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ApiVersionReader = new HeaderApiVersionReader(HeaderNames.ApiVersion);
});
builder.Services.AddScoped<IVersionManager, VersionManager>();

// 路由配置...
builder.Services.Configure<RouteOptions>(options =>
{
    options.LowercaseUrls = true;
    options.LowercaseQueryStrings = true;
});

步骤2:让Minimal API使用Newtonsoft.Json序列化

修改/version端点的返回逻辑,手动使用Newtonsoft.Json序列化后返回:

app.MapGet("/version", ([FromServices] IVersionManager versionManager, [FromQuery] string productName, [FromQuery] Version productVersion) =>
{
    ValidationResult<VersionCheckResponse> result = versionManager.CheckVersion(productName, productVersion);
    var response = result.IsValid 
        ? new ApiResponse<VersionCheckResponse>(result.Value) 
        : new ApiResponse<VersionCheckResponse>(result.ValidationMessage, result.ValidationErrors);

    // 使用Newtonsoft.Json手动序列化
    var json = JsonConvert.SerializeObject(response, new JsonSerializerSettings
    {
        Converters = { new VersionConverter() }
    });
    return Results.Text(json, "application/json");
});

步骤3:确保实体上的特性正确

保留VersionCheckResponse上的Newtonsoft特性:

public class VersionCheckResponse
{
    [Newtonsoft.Json.JsonConverter(typeof(VersionConverter))]
    public Version Version { get; set; }
}

方案2:统一使用System.Text.Json处理所有序列化

步骤1:修复Program.cs中的重复注册问题

同样删除重复的builder.Services.AddControllers();,并全局配置System.Text.Json:

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

// 配置Controllers使用自定义转换器
builder.Services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Converters.Add(new VersionJsonConverter());
    // 可选:保持和旧行为一致的其他配置,如忽略null值等
    options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});

// 全局配置System.Text.Json,让Minimal API也使用该配置
builder.Services.Configure<JsonOptions>(options =>
{
    options.SerializerOptions.Converters.Add(new VersionJsonConverter());
    options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});

// 其他服务注册...
builder.Services.AddApiVersioning(options =>
{
    options.ReportApiVersions = true;
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ApiVersionReader = new HeaderApiVersionReader(HeaderNames.ApiVersion);
});
builder.Services.AddScoped<IVersionManager, VersionManager>();

// 路由配置...
builder.Services.Configure<RouteOptions>(options =>
{
    options.LowercaseUrls = true;
    options.LowercaseQueryStrings = true;
});

步骤2:更新实体特性为System.Text.Json的特性

替换VersionCheckResponse上的特性:

public class VersionCheckResponse
{
    [System.Text.Json.Serialization.JsonConverter(typeof(VersionJsonConverter))]
    public Version Version { get; set; }
}

步骤3:Minimal API端点无需修改

此时Results.Ok会自动使用全局配置的System.Text.Json转换器,直接保留原有逻辑即可:

app.MapGet("/version", ([FromServices] IVersionManager versionManager, [FromQuery] string productName, [FromQuery] Version productVersion) =>
{
    ValidationResult<VersionCheckResponse> result = versionManager.CheckVersion(productName, productVersion);
    if (result.IsValid)
    {
        return Results.Ok(new ApiResponse<VersionCheckResponse>(result.Value));
    }

    return Results.BadRequest(new ApiResponse<VersionCheckResponse>(result.ValidationMessage, result.ValidationErrors));
});

额外注意事项

  • 如果需要处理反序列化(如QueryString中的Version参数),System.Text.Json的转换器已经包含了字符串格式的解析逻辑,无需额外处理;Newtonsoft.Json则需要确保QueryString绑定使用Newtonsoft(可通过添加[FromQuery] [Newtonsoft.Json.JsonConverter(typeof(VersionConverter))] Version productVersion实现)。
  • 测试时务必清除浏览器缓存或使用Postman等工具的"禁用缓存"选项,避免旧响应干扰测试结果。

内容的提问来源于stack exchange,提问作者Walter Williams

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 13:44:52