You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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模型绑定与序列化规则变更

  1. 参数绑定规则收紧:
    .NET 5中,[HttpPut]方法的多个参数会自动分配绑定源——复杂类型(List<View>)从请求体绑定,简单类型(UserLogin)从路由绑定。但.NET 6默认启用了更严格的绑定逻辑,当方法存在多个参数时,必须显式指定每个参数的绑定源,否则框架无法正确解析请求体中的复杂类型,触发400错误。

  2. 只读属性反序列化限制:
    View类的fullname是只读计算属性(仅表达式体getter),且标记了[NotMapped]。.NET 5中System.Text.Json默认序列化只读属性,但.NET 6默认改为不序列化/反序列化只读属性。而请求JSON中包含了fullname字段,框架尝试反序列化时,因该属性无setter导致绑定失败,引发400错误。

  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 08:03:18