使用.NET Core模型绑定JSON Post到Web API

20

我刚开始使用.NET Core做一个新项目。添加了Web API控制器和相关方法。我使用Postman创建了一个JSON对象并将其发布到我的控制器方法。请注意,JSON对象与控制器方法中的Object参数完全匹配。

在调试模式下,我可以看到该对象不为空,属性也都存在,但是属性值被默认为它们所代表的类型,例如int默认为0等。

我以前从未见过这种行为……所以我使用完全相同的代码和对象,在一个MVC项目中复制了一个Web API 2控制器,并且它完美地工作。

我错过了什么,我不能在.NET Core中发布JSON并进行模型绑定吗?

阅读本文后,我发现除非我将其作为表单POST或查询字符串变量发送,否则我无法这样做。顺便说一下,这两种方法都可以正常工作。

https://lbadri.wordpress.com/2014/11/23/web-api-model-binding-in-asp-net-mvc-6-asp-net-5/

JSON:

{
   "id": "4",
   "userId": "3"
   "dateOfTest": "7/13/2017"
}

方法:

[HttpPost]
[Route("test1")]
[AllowAnonymous]
public IActionResult Test(Class1 data)
{
    return Ok();
}

只是想补充一下,如果你正在使用核心3.1及以上版本,则默认使用System.Text.Json进行转换。目前,它仅序列化/反序列化属性,因此您的类需要使用属性而不是字段,就像edincorbin的答案中所述。 - Anand Mukundan
6个回答

36

注意:如果您正在使用aspnet core 3.0,则可以在此处找到解决方案对于其他版本,请继续阅读。

您需要使用FromBody属性将参数标记为来自正文,如下所示:

[HttpPost]
[Route("test1")]
[AllowAnonymous]
public IActionResult Test([FromBody] Class1 data)
{
    return Ok();
}
你需要确保在Postman中使用application/json作为你的内容类型:

Postman application/json

结果将会是:

json POST action

同时,确保你的属性设置器是公共的。
public class Person
{
    public String Name;
    public Int32 Age;
}

那就是我最初的设置,而且对象Class1为空。 - Mike Anderson
现在它能工作了吗?我会编辑答案,只是为了澄清标题。 - Matt
@MikeAnderson:假设你有[FromBody]data为空的唯一原因是没有一个发布的名称与Class1上的属性匹配。如果类上至少有一个属性具有值,则模型绑定器将仅实例化该类(即有东西可以实例化)。 - Chris Pratt
谢谢澄清。我现在明白问题了。我试图绑定的对象是一个EF生成的对象。当我只创建类而没有添加EF构造函数和虚拟属性时,它就像魔术一样工作。所以我想为了让这个在CORE中工作,我不能使用EF生成的持久化模型,我必须创建自己的DTO或VM。这也是最佳实践...谢谢! - Mike Anderson
1
感谢您在 .NET Core 3.0 上的精彩笔记。我已经搜索了整整一天,试图找出问题所在,直到看到您的笔记才解决了问题。非常感谢! - jmgardn2

8

我也曾经遇到过这个问题,想和大家分享一个必要的步骤,它在其他回答中没有提到。我需要在类中的每个属性后面添加 {get; set;},如下所示:

    public class LogString{
        public string val {get; set;}
        public string data {get; set;}
    }

其余部分相同:
        [HttpPost]
        public void Post([FromBody] LogString message)
        {      
            Console.WriteLine(message.val);
        }

在添加了那个后,它开始正常工作。

2
这是唯一对我有用的东西。我在文档中没有看到任何地方说需要使用属性而不是成员变量!这真让人沮丧。 - adam_0
一个类中所有属性都缺少了 Agrrr {get; set;},浪费了一个小时!谢谢! - dotsa

6
我有一个ASP.Net Core 3.1应用程序,对我来说,这些解决方案都没有起作用。在我找到文档之前,我浪费了一些时间(和工作几个小时)。 https://learn.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-3.1
  1. 安装 Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet 包。

  2. 更新项目的 Startup.ConfigureServices 方法,调用 AddNewtonsoftJson。例如:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages()
            .AddNewtonsoftJson();//这是重要的一步
    }
    

就是这样!我不需要[FromBody]属性。

如果不起作用,请验证你的模型属性是否有设置器(例如public string Message { get; set; })。不要忘记设置器。


2
请使用绑定对象的属性而不是字段
在.NET 5中,我不得不为我的公共字段添加{get; set;},将它们转换为属性以使它们能够填充。
无论是.NET 5的模型绑定器不能处理字段还是您需要进行特殊配置才能使其填充字段,我都不清楚。将所有字段转换为属性需要花费几分钟时间,因此我不打算花时间尝试弄清楚如何配置它以处理字段。
注意:如果您遇到请求对象的此问题,则可能也会遇到响应对象的相同问题。

转换为.NET 5并花了2个小时理解为什么事情不再起作用。 {get; set;}是一个类中的罪犯。谢谢。 - dotsa

1
我已经为此问题苦苦挣扎了几个小时,但现有的解决方案都对我无效,结果发现我没有为传入实例的类提供公共无参数构造函数。在这种情况下,Class1只有受保护的构造函数,并且无论如何参数始终为null。希望能对某些人有所帮助。

0

如果有其他人来到这个问题,和我一样习惯于使用 .Net Framework MVC,你可能会创建类似于这样的 JSON 对象 -

 this.http.post<Stock>("/api/Stock2", { Stock: this.SelectedStock }, this.httpOptions)

然后期望它与“股票”相绑定

[HttpPost("Stock2")]
    public JsonResult Stock2(ConsolidatedStock Stock) {
        return this.Json(1);
    }

这在 .net core 中将不再起作用,您需要更改构建 JSON 的方式 -

 this.http.post<Stock>("/api/Stock2", this.SelectedStock, this.httpOptions)

如果你想发布多个对象,你需要创建一个新的模型来存储这些对象,例如

public class StockViewModel {
    public ConsolidatedStock Stock { get; set; }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接