如何在ASP.NET Core API中全局处理值类型可空与空引用异常
ASP.NET Core API 两类参数崩溃问题解决方案
1. 值类型参数接收null导致崩溃问题
把所有值类型批量改成可空值类型不是合理方案,工作量大且会引入大量无意义的空判断逻辑,属于典型的过度改造,优先用以下零/低侵入方案解决:
根源全局处理(零模型代码修改,推荐)
崩溃本质是默认JSON反序列化逻辑不允许给非空值类型赋值null,直接在Program.cs中配置全局JSON序列化规则即可,无需改动任何业务模型代码:
builder.Services.AddControllers() .AddJsonOptions(options => { // 注册自定义null转默认值转换器,从反序列化层拦截null赋值 options.JsonSerializerOptions.Converters.Add(new NullToDefaultValueConverter()); });
配套的通用JSON转换器代码如下:
public class NullToDefaultValueConverter : JsonConverterFactory { public override bool CanConvert(Type typeToConvert) { // 仅匹配int/long/double/decimal等非可空值类型 return typeToConvert.IsValueType && Nullable.GetUnderlyingType(typeToConvert) == null; } public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) { return (JsonConverter)Activator.CreateInstance( typeof(InnerNullDefaultConverter<>).MakeGenericType(typeToConvert))!; } private class InnerNullDefaultConverter<T> : JsonConverter<T> where T : struct { public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 遇到null直接返回值类型默认值(0/0.0/false等),不抛出反序列化异常 if (reader.TokenType == JsonTokenType.Null) return default; return JsonSerializer.Deserialize<T>(ref reader, options); } public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, options); } } }
如果不想给null自动赋默认值,而是直接返回参数校验错误,你提到的自定义特性方案完全可行,注意你示例里的特性名存在拼写错误(CheckIIntllegaType应为CheckIllegalType),参考实现如下:
// 自定义校验特性 public class CheckIllegalTypeAttribute : ValidationAttribute { public override bool IsValid(object value) { if (value == null) { ErrorMessage = "当前参数不允许传递null值"; return false; } return true; } } // 使用方式,直接打在对应属性上即可 public class TestModel { [CheckIllegalType] public int testId { get; set; } }
该方案需要给对应属性加标注,工作量比全局JSON配置大,仅适合需要精细化控制参数校验规则的场景。如果项目用Newtonsoft.Json做序列化,逻辑完全一致,只要把转换器注册到Newtonsoft的序列化选项中即可。
2. 嵌套模型空引用崩溃问题
无需逐一给所有嵌套访问加?.判空,用以下两种通用方案即可解决:
全局异常兜底(零业务代码修改,推荐)
在请求管道最前端加全局异常捕获中间件,统一拦截所有空引用异常,返回标准化错误响应,避免接口直接抛出500错误:
// 注意要放在所有中间件的最前面,保证能捕获所有管道内的异常 app.Use(async (context, next) => { try { await next(); } catch (NullReferenceException) { context.Response.StatusCode = StatusCodes.Status400BadRequest; await context.Response.WriteAsJsonAsync(new { Code = 400, Msg = "请求参数不合法,存在缺失的嵌套对象字段" }); } });
编译期空检查(长期维护推荐)
在项目csproj文件中开启C#可空引用类型上下文,编译阶段会自动识别所有未做判空的引用类型访问,插入隐式空检查逻辑,从根源避免空引用崩溃:
<PropertyGroup> <Nullable>enable</Nullable> </PropertyGroup>
开启后所有未标注?的引用类型(包括嵌套子模型),编译器会自动做非空校验,子模型为null时会直接抛出可控的参数验证异常,配合全局异常处理即可实现全链路防护。
内容的提问来源于stack exchange,提问作者Abdul Aleem Solutions
相关产品推荐
相关产品推荐

