如何针对单个Web请求配置System.Text.Json的PropertyNamingPolicy
基于请求头切换System.Text.Json的命名规则(PascalCase/CamelCase)
问题背景
我们正将代码库从Newtonsoft.Json迁移至System.Text.Json,需实现根据Web请求的Accept头动态切换JSON命名规则:
- 遗留ASP.NET网站发送
application/json;case=default的Accept头,要求返回PascalCase格式的JSON - 新Angular网站使用默认的CamelCase格式
此前使用Newtonsoft.Json时,我们通过自定义输出格式化器实现该逻辑:
public class NewtonsoftJsonDefaultCaseFormatter : NewtonsoftJsonOutputFormatter { public NewtonsoftJsonDefaultCaseFormatter(List<JsonConverter> converters) : base(new JsonSerializerSettings { ContractResolver = new DefaultContractResolver(), NullValueHandling = NullValueHandling.Include }, ArrayPool<char>.Shared, new MvcOptions(), new MvcNewtonsoftJsonOptions()) { foreach (var converter in converters) SerializerSettings.Converters.Add(converter); SupportedMediaTypes.Clear(); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json;case=default")); } }
目前已在Startup中设置默认CamelCase序列化配置,但需要针对特定Accept头的请求切换为PascalCase:
var mvcBuilder = services .AddControllers() .AddJsonOptions(options => GetDefaultJsonOptions()); public static JsonSerializerOptions GetDefaultJsonOptions() { var defaultOptions = new JsonSerializerOptions(); defaultOptions.Converters.Add(new ConcurrentBagConverterFactory()); defaultOptions.Converters.Add(new DateTimeConverter()); defaultOptions.Converters.Add(DictionaryJsonConverterFactory.Default); defaultOptions.Converters.Add(new SqlGeographyConverter()); defaultOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; defaultOptions.PropertyNameCaseInsensitive = true; defaultOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; defaultOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; return defaultOptions; }
解决方案
要在System.Text.Json中实现相同逻辑,需自定义SystemTextJsonOutputFormatter并注册到Mvc的格式化器集合中:
1. 自定义PascalCase输出格式化器
继承SystemTextJsonOutputFormatter,基于全局配置创建PascalCase的序列化选项,并指定仅支持目标Accept头:
public class PascalCaseJsonOutputFormatter : SystemTextJsonOutputFormatter { public PascalCaseJsonOutputFormatter(JsonSerializerOptions baseOptions) : base(CreatePascalCaseOptions(baseOptions)) { SupportedMediaTypes.Clear(); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json;case=default")); } private static JsonSerializerOptions CreatePascalCaseOptions(JsonSerializerOptions baseOptions) { // 复制全局配置,避免修改原选项 var pascalOptions = new JsonSerializerOptions(baseOptions); // 设置为PascalCase(null表示不修改属性名,保持原命名) pascalOptions.PropertyNamingPolicy = null; return pascalOptions; } }
2. 在Startup中注册格式化器
修改Startup配置,添加自定义格式化器,确保ASP.NET Core能根据Accept头自动选择对应的序列化规则:
var mvcBuilder = services.AddControllers(); // 配置默认CamelCase序列化 mvcBuilder.AddJsonOptions(options => { var defaultOptions = GetDefaultJsonOptions(); options.JsonSerializerOptions.Converters.AddRange(defaultOptions.Converters); options.JsonSerializerOptions.PropertyNamingPolicy = defaultOptions.PropertyNamingPolicy; options.JsonSerializerOptions.PropertyNameCaseInsensitive = defaultOptions.PropertyNameCaseInsensitive; options.JsonSerializerOptions.DefaultIgnoreCondition = defaultOptions.DefaultIgnoreCondition; options.JsonSerializerOptions.ReferenceHandler = defaultOptions.ReferenceHandler; }); // 注册自定义PascalCase格式化器 mvcBuilder.AddMvcOptions(options => { var defaultJsonOptions = options.JsonSerializerOptions; options.OutputFormatters.Add(new PascalCaseJsonOutputFormatter(defaultJsonOptions)); }); public static JsonSerializerOptions GetDefaultJsonOptions() { var defaultOptions = new JsonSerializerOptions(); defaultOptions.Converters.Add(new ConcurrentBagConverterFactory()); defaultOptions.Converters.Add(new DateTimeConverter()); defaultOptions.Converters.Add(DictionaryJsonConverterFactory.Default); defaultOptions.Converters.Add(new SqlGeographyConverter()); defaultOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; defaultOptions.PropertyNameCaseInsensitive = true; defaultOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; defaultOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; return defaultOptions; }
核心要点
- 自定义格式化器通过复制全局
JsonSerializerOptions继承所有转换器和配置,仅修改命名规则,保证配置一致性 - 清除默认支持的媒体类型,仅响应
application/json;case=default请求,避免干扰正常CamelCase请求 - ASP.NET Core的内容协商机制会自动根据请求的Accept头匹配对应的输出格式化器,无需额外路由或逻辑判断
内容的提问来源于stack exchange,提问作者Justin
相关产品推荐
相关产品推荐

