.NET 7中BadRequestObjectResult仍使用驼峰命名策略问题
解决BadRequestObjectResult返回ODataError时的命名格式问题
这问题出在OData的错误响应序列化逻辑上——你全局配置的JsonSerializerOptions并没有被BadRequestObjectResult处理ODataError时复用,它走了OData自己的默认序列化逻辑,所以还是驼峰命名。下面给你几个可行的解决办法:
办法1:用JsonResult替代BadRequestObjectResult(最简单)
直接把返回类型换成JsonResult,同时传入你全局配置的JsonSerializerOptions,这样就能严格遵循你设置的命名规则,还能保持400的错误状态码:
protected ActionResult ValidationFailed() { var err = new ODataError { Message = "Validation error", Details = ModelState.ToDictionary( kvp => kvp.Key, kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray() ).Select(e => new ODataErrorDetail() { ErrorCode = e.Key, Message = string.Join("\n", e.Value.ToArray()) }).ToList() }; // 拿到全局配置的Json序列化选项 var jsonOptions = HttpContext.RequestServices.GetRequiredService<IOptions<JsonOptions>>().Value.JsonSerializerOptions; // 返回JsonResult并指定状态码为400 return new JsonResult(err, jsonOptions) { StatusCode = StatusCodes.Status400BadRequest }; }
办法2:全局配置OData错误序列化(一劳永逸)
如果想让所有OData错误都统一用你的命名规则,可以在配置OData的时候自定义错误序列化器,让它复用全局的JSON配置:
首先添加自定义的ODataErrorSerializer:
public class CustomODataErrorSerializer : ODataErrorSerializer { private readonly JsonSerializerOptions _globalJsonOptions; public CustomODataErrorSerializer(JsonSerializerOptions jsonOptions) : base(ODataPayloadKind.Error) { _globalJsonOptions = jsonOptions; } public override async Task WriteObjectAsync(object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext) { if (graph is ODataError error) { // 用全局配置序列化错误对象 var jsonWriter = messageWriter.CreateODataJsonWriter(); await JsonSerializer.SerializeAsync(jsonWriter, error, _globalJsonOptions); await jsonWriter.FlushAsync(); return; } await base.WriteObjectAsync(graph, type, messageWriter, writeContext); } }
然后在Startup/Program里配置OData时注册这个序列化器:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; options.JsonSerializerOptions.DictionaryKeyPolicy = null; }) .AddOData(options => { options.Select().Filter().OrderBy().Expand().Count().SetMaxTop(null); options.AddRouteComponents("odata", GetEdmModel(), services => { var globalJsonOptions = services.BuildServiceProvider().GetRequiredService<IOptions<JsonOptions>>().Value.JsonSerializerOptions; services.AddSingleton<ODataSerializerProvider>(sp => new DefaultODataSerializerProvider(sp) { ODataErrorSerializer = new CustomODataErrorSerializer(globalJsonOptions) }); }); });
办法3:自定义错误类包装ODataError(繁琐不推荐)
如果你不想改返回方式或全局配置,也可以自己写个错误类,用[JsonPropertyName]强制指定序列化后的键名,再把ODataError包进去返回:
public class CustomErrorResponse { [JsonPropertyName("Error")] public ODataError Error { get; set; } } protected ActionResult ValidationFailed() { var err = new ODataError { Message = "Validation error", Details = ModelState.ToDictionary( kvp => kvp.Key, kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray() ).Select(e => new ODataErrorDetail() { ErrorCode = e.Key, Message = string.Join("\n", e.Value.ToArray()) }).ToList() }; return new BadRequestObjectResult(new CustomErrorResponse { Error = err }); }
效果验证
用办法1修改后,返回的响应就会和你预期的一致:
{ "Error": { "Code": "", "Message": "Validation error", "Details": [ { "Code": "Person.Corpid", "Message": "Corpid field must be value" } ] } }
内容的提问来源于stack exchange,提问作者user2748122
相关产品推荐
相关产品推荐

