混淆了 ASP.NET Core 中的 FromBody

38

我有以下的WEB API方法,并且有一个使用Angular的SPA模板:

[HttpPost]
public IActionResult Post([FromBody]MyViewModel model)
我认为,根据这个话题,由于我想要从消息正文中读取值,所以在此不需要使用[FromBody]覆盖默认行为,但是如果我不使用[FromBody],那么来自Angular的模型将为空。 我很困惑,既然我已经使用了默认行为,为什么还应该使用[FromBody]呢?

如果你正在使用Angular端从.net Core调用API,你必须使用[FromBody] - Niladri
2
@Niladri 好的,我知道。但是为什么? - user5032790
请检查Stephen提供的链接...这是由于.NET Core中的JSON模型绑定。对于查询字符串,您必须在HttpGet中使用[FromQuery] - Niladri
1
在ASP.NET Core 2.1中,如果在Post请求中明确将请求体分配给该参数,则此属性可能是可选的。 - Ray
4个回答

42

针对看到此问题的任何人——.net core 3版本,您需要在扩展ControllerBase的控制器中添加[ApiController]标记。如果您使用MVC控制器,只需要添加[FromBody]标记。

这将自动处理请求正文,以满足您的期望方式。

Microsoft ApiController属性文档


我在两个项目中遇到了相同的问题,是否有什么我在从2.1-> 2.2-> 3.0-> 3.1的迁移文档中错过的东西? - Mathias Haugsbø
这是否意味着我需要在任何路由中添加[ApiController]?这太麻烦了! - xSx
1
我找了很久才找到这个!谢谢。我的.NET Core Web API 3.1项目中,我没有在我的控制器上添加[ApiController]。然后我需要[FromBody]。现在已经添加了[ApiController],所以它可以在不使用[FromBody]的情况下正常工作。 - duyn9uyen
我欠你一整天呢!你救了我!! - Onur Omer
这是不正确的。它与使用的内容类型有关。为了在 ASP.NET Core 中正确绑定 JSON,您必须修改您的操作以在参数上包含属性 [FromBody]。这告诉框架使用请求的内容类型标头来决定哪个配置的 IInputFormatters 用于模型绑定。https://andrewlock.net/model-binding-json-posts-in-asp-net-core/ - user10285265

13
您链接到的问题是关于 Web API 的。您正在使用已被重写以将以前的 MVC 和 Web API 版本的管道合并为一个 Controller 类的 Core MVC。
当发布json(而不是x-www-form-urlencoded)时,需要使用[FromBody]属性来指示ModelBinder使用内容类型标头来确定要用于读取请求的IInputFormatter
有关在 core-mvc 中将模型绑定到 json 的详细说明,请参见Model binding JSON POSTs in ASP.NET Core

2
"最初的回答" 翻译成英文是 "Original Answer"。
以下是需要翻译的内容:

假设您需要在控制器API中同时支持[FromForm][FromBody],这里有一种替代方法...

前端(Angular代码):

forgotPassword(forgotPassword: ForgotPassword): Observable<number> {
  const params = new URLSearchParams();
  Object.keys(forgotPassword).forEach(key => params.append(key, forgotPassword[key]));
  return this.httpClient.post(`${this.apiAuthUrl}/account/forgotpassword`, params.toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });
}

后端(C#代码):

最初的回答
[AllowAnonymous]
[HttpPost("[action]")]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model) { }

现在你的签名可以保持不变,以便支持两者。另外,我考虑到了一种更加永久的方法,同时也解决了这个问题。希望这篇文章能对某些人有所帮助!

1

请查看我的讨论https://dev59.com/BVcP5IYBdhLWcg3wXIsy#75263628关于[FromBody]。它详细解释了一切!

但总的来说...

Angular,像所有JavaScript API一样,不会发布传统的HTML表单字段数据(在HttpRequest正文中的名称-值对)。这些JavaScript API使用XMLHttpRequest-type POSTS,超越了传统的基于HTML浏览器的表单POST,并直接将数据发送到服务器中的特殊JSON Content-type消息中。

Angular人员没有告诉您的是,这不是HTML类型的<form>数据POST。Angular 劫持了Web浏览器中的所有表单字段POST调用,并向服务器发送自己的数据。

现代Web API端点中有很多不支持接收Angular HttpRequests作为特殊的POST JSON格式,除非附加了关于所接收到的建模数据类型的额外“提示”。请记住,JSON只是一个普通的文本字符串,只有通过JSON mime-typeContent-type Http头信息来标识为“特殊文本”! JSON对象只是打包成这些特殊格式化文本字符串的数据。但大多数服务器并不知道这一点。有些服务器知道。

但这就是为什么在ASP.NET CoreWebAPI Core中我们现在有两个装饰器来帮助我们:[FromBody][FromForm]

这就是ASP.NET Core或WebAPI中[FromBody]的作用。它告诉端点监听以对象形式传入的JSON数据或文本字符串,并帮助将它们转换为ASP.NET端点方法参数列表中的建模数据类型。

[FromForm]是相反的,它是为ASP.NET MVC和更传统的HTML名称-值数据提交到服务器而设计的,不需要所有这些额外的JavaScript和JSON格式化。如果您要从纯HTML网页向服务器发布普通HTML <form>数据,请使用它。

所以再次强调...[FromBody]不接受HTML表单字段名-值对,例如[FromForm]。它是为像Angular这样发送JSON数据的API设计的。

由于您正在使用Angular,因此应该用[FromBody]设置服务器端点。

请记住,在客户端的Angular端,现在向服务器发送数据需要以下步骤:

  1. JavaScript手动发送POST请求到Web API服务器
  2. JavaScript带有JSON MIME类型附加的HTTP头
  3. JavaScriptHTTP正文提取的表单字段数据,重新格式化并作为JSON提交。传统的HTML POST名称-值对将不起作用!

然而,由于AngularObservablesHttpClientModule调用已经为您处理了所有这些问题,所以您应该可以顺利进行!但是,如果您没有使用Angular,则上面的列表将适用于您自定义的JavaScript调用,向新端点发送JSON。


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