.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端点时始终使用默认序列化逻辑,无法触发自定义逻辑。
核心原因
- Minimal API与Controllers配置隔离:Minimal API的
Results.Ok等返回方法默认使用全局System.Text.Json配置,而非Controllers中单独配置的序列化规则(不管是Newtonsoft还是System.Text.Json)。 - 重复注册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
相关产品推荐
相关产品推荐

