.NET 6使用.NET 5相同方法签名调用时返回400错误请求
我在控制器中编写了如下用于编辑数据库对象的方法:
[HttpPut] [Route("PutDelete/{UserLogin}")] public async Task<bool> PutDelete(List<View> viewlist, string UserLogin) { // some code }
调用方式如下:
await Http.PutJsonAsync(baseUrl + "/api/View/putDelete/" + SessionValue_user, viewlist);
其中viewlist的类型为List<View>。
该代码在.NET 5中运行正常,方法可接收对象列表并执行标记删除操作,但在.NET 6中执行相同操作时,出现「Bad Request」及400错误码。请求URI和序列化后的JSON数据与.NET 5中一致,未发现差异。
View类定义:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using System.ComponentModel.DataAnnotations.Schema; namespace Some.Namespace { public class View { public string id { get; set; } #nullable enable public DateTime? orderedfordatetimeutc { get; set; } public DateTime? creationdateutc { get; set; } public string? locationid { get; set; } public string? firstname {get; set;} public string? lastname {get; set;} [NotMapped] public string? fullname => firstname + ", " + lastname; } }
发送的JSON数据:
"[ { \"orderedfordatetimeutc\":\"2023-06-10T00:00:00\", \"creationdateutc\":\"2023-06-10T00:00:00\", \"locationid\":\"UEB\", \"lastname\":\"Smith\", \"firstname\":\"John\", \"fullname\":\"John, Smith\" } ]"
核心原因:.NET 6模型绑定与序列化规则变更
参数绑定规则收紧:
.NET 5中,[HttpPut]方法的多个参数会自动分配绑定源——复杂类型(List<View>)从请求体绑定,简单类型(UserLogin)从路由绑定。但.NET 6默认启用了更严格的绑定逻辑,当方法存在多个参数时,必须显式指定每个参数的绑定源,否则框架无法正确解析请求体中的复杂类型,触发400错误。只读属性反序列化限制:
View类的fullname是只读计算属性(仅表达式体getter),且标记了[NotMapped]。.NET 5中System.Text.Json默认序列化只读属性,但.NET 6默认改为不序列化/反序列化只读属性。而请求JSON中包含了fullname字段,框架尝试反序列化时,因该属性无setter导致绑定失败,引发400错误。Nullable引用类型检查:
View类的id字段为非可空string类型,但请求JSON中未包含id值。.NET 6默认启用Nullable引用类型检查,非空字段缺失值会触发模型验证失败,也是400错误的潜在原因。
解决步骤
1. 显式指定参数绑定源
修改控制器方法,给参数添加绑定源特性,明确告诉框架参数的来源:
[HttpPut] [Route("PutDelete/{UserLogin}")] public async Task<bool> PutDelete([FromBody] List<View> viewlist, [FromRoute] string UserLogin) { // some code }
2. 处理只读属性的反序列化问题
可选两种方案:
- 方案一:移除请求中的
fullname字段:fullname是前端可计算的属性,无需从前端传入。修改前端代码,序列化viewlist时排除该字段。 - 方案二:配置Json序列化规则:
在Program.cs中配置System.Text.Json,允许反序列化只读属性:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.Never; options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; });
或给fullname属性添加[JsonInclude]特性,明确允许序列化/反序列化:
[NotMapped] [JsonInclude] public string? fullname => firstname + ", " + lastname;
3. 修复Nullable引用类型问题
给id字段添加可空修饰符,确保符合Nullable规则:
public string? id { get; set; }
或确保请求JSON中包含id字段的值。
内容的提问来源于stack exchange,提问作者Comfortable_Onion

