找回密码
 会员注册
查看: 134|回复: 0

模型绑定ASP.NET Core中的JSON POST(你的.net core 为什么无法接收JSON参

[复制链接]

250

主题

1

回帖

819

积分

管理员

积分
819
发表于 2024-2-29 08:47:58 | 显示全部楼层 |阅读模式

有一天,当我出现一个关于模型绑定的问题时,我正在追赶最新的ASP.NET社区Standup,这个问题我之前没有接过(你可以在46:30左右看到这个问题)。它指出在ASP.NET Core(ASP.NET 5 的新名称)中,您不能再简单地将JSON数据发布到MVC控制器并自动绑定它,您以前可以在ASP.NET 4 / MVC中执行此操作5。

在这篇文章中,我将展示如果您将项目转换为ASP.NET Core并且发现您的JSON POST不起作用该怎么办。我将演示MVC 5模型绑定和MVC Core模型绑定之间的区别,突出显示两者之间的差异,以及如何根据您期望的数据为您的项目设置控制器。

TL; DR:将[FromBody]属性添加到ASP.NET Core控制器操作中的参数注意,如果您使用的是ASP.NET Core 2.1,则还可以使用该[ApiController]属性自动推断[FromBody]复杂操作方法参数的绑定源。有关详细信息,请参阅文档

我的数据在哪里?

想象一下,您已经创建了一个闪亮的新ASP.NET核心项目,您正在使用它来重写现有的ASP.NET 4应用程序(当然只是出于明智的原因!)您将旧的WebApi控制器复制并粘贴到.NET Core Controller中,清理命名空间,测试GET操作,一切似乎都运行良好。

注意:在ASP.NET 4中,尽管MVC和WebApi管道的行为非常相似,但它们完全是分开的。因此,您分别拥有WebApi和Mvc的单独ApiController和Controller类(以及所有相关的命名空间混淆)。在ASP.NET Core中,管道都已合并,只有单个Controller类。

当您的GET请求正常工作时,您知道您的大部分管道(例如路由)可能已正确配置。您甚至可以提交一个测试表单,该表单将一个发送POST到控制器并接收它发回的JSON值。一切都很好看。

将x-www-url-formencoded内容发布到ASP.NET Core控制器

作为拼图的最后一部分,你测试发送一个POST带有JSON数据的AJAX ,它们都崩溃了 - 你收到了一个200 OK,但你对象上的所有属性都是空的。但为什么?

将JSON内容发布到ASP.NET Core控制器

什么是模型绑定?

在我们详细了解这里发生的事情之前,我们需要对模型绑定有一个基本的了解。模型绑定是MVC或WebApi管道获取原始HTTP请求并将其转换为控制器上的操作方法调用的参数的过程。

例如,考虑以下WebApi控制器和Person类:

  1. public class PersonController : ApiController
  2. {
  3. [HttpPost]
  4. public Person Index(Person person)
  5. {
  6. return person;
  7. }
  8. }
  9. public class Person
  10. {
  11. public string FirstName { get; set; }
  12. public string LastName { get; set; }
  13. public int Age { get; set; }
  14. }
复制代码

我们可以看到控制器上有一个动作方法,一个POST动作,它接受一个参数 - 一个Person类的实例。然后控制器只是回显该对象,然后回到响应中。

那么Person参数来自哪里?模型绑定救援!模型绑定器可以在许多不同的位置查找数据以便水合人物对象。模型绑定器具有高度可扩展性,并允许自定义实现,但常见绑定包括:

  • 路由值 - 导航到诸如{controller}/{action}/{id}允许绑定到id参数的路由
  • 查询字符串 - 如果您已将变量作为查询字符串参数传递,例如?FirstName=Andrew,则FirstName可以绑定参数。
  • 正文 - 如果您在帖子的正文中发送数据,则可以将其绑定到Person对象
  • 标头 - 您也可以绑定到HTTP标头值,尽管这种情况不太常见。

因此,您可以看到有多种方法可以将数据发送到服务器,并让模型绑定器自动为您创建正确的方法参数。有些需要explcit配置,而有些则需要免费获得。例如,路径值和查询字符串参数始终是绑定的,对于复杂类型(即不是像string或的基元int),主体也是绑定的。

重要的是要注意,如果模型绑定器由于某种原因未能绑定参数,它们将不会抛出错误,而是您将收到一个默认对象,没有设置任何属性,这是我们之前显示的行为。

它在ASP.NET 4中的工作原理

为了解决这里发生的事情,我创建了两个项目,一个使用ASP.NET 4,另一个使用最新的ASP.NET Core(在编写本文时非常接近RC2)。你可以在这里和这里的 GitHub上找到它们。

在ASP.NET WebApi项目中,有一个简单的控制器,它接受一个Person对象并简单地返回该对象,如我在上一节中所示。

在一个简单的网页上,我们然后制作POSTs(为方便起见使用jQuery),发送请求x-www-form-urlencoded(正如您从普通表单中获得的POST)或作为JSON。

  1. //form encoded data
  2. var dataType = 'application/x-www-form-urlencoded; charset=utf-8';
  3. var data = $('form').serialize();
  4. //JSON data
  5. var dataType = 'application/json; charset=utf-8';
  6. var data = {
  7. FirstName: 'Andrew',
  8. LastName: 'Lock',
  9. Age: 31
  10. }
  11. console.log('Submitting form...');
  12. $.ajax({
  13. type: 'POST',
  14. url: '/Person/Index',
  15. dataType: 'json',
  16. contentType: dataType,
  17. data: data,
  18. success: function(result) {
  19. console.log('Data received: ');
  20. console.log(result);
  21. }
  22. });
复制代码

这将为编码POST类似于(简称为简洁)的表单创建HTTP请求:

  1. POST /api/Person/UnProtected HTTP/1.1
  2. Host: localhost:5000
  3. Accept: application/json, text/javascript, */*; q=0.01
  4. Content-Type: application/x-www-form-urlencoded; charset=UTF-8
  5. FirstName=Andrew&LastName=Lock&Age=31
复制代码

并为JSON帖子:

  1. POST /api/Person/UnProtected HTTP/1.1
  2. Host: localhost:5000
  3. Accept: application/json, text/javascript, */*; q=0.01
  4. Content-Type: application/json; charset=UTF-8
  5. {"FirstName":"Andrew","LastName":"Lock","Age":"31"}
复制代码

发送这两个POSTs会引发以下控制台响应:

ASP.NET 4控制器成功发布的图片

在这两种情况下,控制器都绑定到HTTP请求的主体,并且我们发送的参数被返回给我们,而我们不必做任何声明性的操作。模型粘合剂为我们做了所有的魔术。请注意,虽然我一直在使用WebApi控制器,但MVC控制器模型绑定器在此示例中的行为相同,并且将绑定两个POSTs。

ASP.NET Core中的新方法

因此,继续使用ASP.NET Core,我们创建了一个类似的控制器,使用与Person以前相同的类作为参数:

  1. public class PersonController : Controller
  2. {
  3. [HttpPost]
  4. public IActionResult Index(Person person)
  5. {
  6. return Json(person);
  7. }
  8. }
复制代码

使用与以前相同的HTTP请求,我们看到以下控制台输出,其中x-www-url-formencoded POST绑定正确,但JSON POST不是。

x-www-url-formencoded帖子绑定正确,但JSON帖子不正确

为了在ASP.NET Core中正确绑定JSON,您必须修改操作以[FromBody]在参数中包含该属性。这告诉框架使用content-type请求的标头来决定使用哪个配置的IInputFormatters进行模型绑定。

默认情况下,当你打电话AddMvc()的Startup.cs,一个JSON格式,JsonInputFormatter被自动配置,但如果需要,你可以添加额外的格式化,例如XML绑定到一个对象。

考虑到这一点,我们的新控制器如下所示:

  1. public class PersonController : Controller
  2. {
  3. [HttpPost]
  4. public IActionResult Index([FromBody] Person person)
  5. {
  6. return Json(person);
  7. }
  8. }
复制代码

而我们的JSON POST现在再次像魔术一样!

JSON绑定与FromBody属性一起正常工作

所以只包括[FromBody]?

因此,如果您认为您可以随时使用[FromBody]您的方法,请抓住您的马匹。让我们看看当您使用x-www-url-formencoded请求命中新端点时会发生什么:

使用x-www-url-formencoded with FromBody属性的不支持的媒体类型

噢亲爱的。在这种情况下,我们特意告诉ModelBinder绑定帖子的主体,即FirstName=Andrew&LastName=Lock&Age=31使用IInputFormatter。不幸的是,JSON格式化程序是我们唯一的格式化程序,并且与我们的内容类型不匹配,因此我们得到415错误响应。

为了专门绑定到表单参数,我们可以删除FromBody属性或添加替代FromForm属性,这两个属性都允许我们的表单数据绑定,但同样会再次阻止JSON绑定。

但是,如果我需要绑定两种数据类型呢?

在某些情况下,您可能需要能够将两种类型的数据绑定到操作。在这种情况下,你有点卡住,因为不可能让相同的终点接收两组不同的数据。

相反,您需要创建两个不同的操作方法,这些方法可以专门绑定您需要发送的数据,然后将处理调用委托给一个公共方法:

  1. public class PersonController : Controller
  2. {
  3. //This action at /Person/Index can bind form data
  4. [HttpPost]
  5. public IActionResult Index(Person person){
  6. return DoSomething(person);
  7. }
  8. //This action at /Person/IndexFromBody can bind JSON
  9. [HttpPost]
  10. public IActionResult IndexFromBody([FromBody] Person person){
  11. return DoSomething(person);
  12. }
  13. private IActionResult DoSomething(Person person){
  14. // do something with the person here
  15. // ...
  16. return Json(person);
  17. }
  18. }
复制代码

 

您可能会发现必须使用两种不同的路径才能实现基本相同的操作。不幸的是,路径显然在模型绑定发生之前映射到动作,因此模型绑定器不能用作鉴别器。如果您尝试将上述两个操作映射到同一路径,则会收到错误消息Request matched multiple actions resulting in ambiguity。有可能创建一个自定义路由来根据标头值调用相应的操作,但很可能只会比它的价值更多的努力!

为什么要改变?

那为什么这一切都改变了?旧方式不简单易行吗?好吧,也许,但是有许多的陷阱需要提防的,尤其是当POST荷兰国际集团基本类型。

根据Damian Edwards在社区站立中的主要原因是出于安全原因,特别是跨站点请求伪造(CSRF)预防。我将在ASP.NET Core中稍后发布关于反CSRF的帖子,但实质上,当模型绑定可以从多个不同的源发生时,就像在ASP.NET 4中那样,默认情况下生成的堆栈不安全。我承认我没有理解为什么现在还是如何被利用,但我认为这与FormToken你从多个来源获取数据时识别你的反CSRF有关。

摘要

简而言之,如果您的模型绑定无法正常工作,请确保它尝试从请求的正确部分进行绑定,并且您已注册了相应的格式化程序。如果它是你正在做的JSON绑定,那么添加[FromBody]你的参数就可以了!

参考

  • https://docs.asp.net/en/latest/mvc/models/model-binding.html
  • https://lbadri.wordpress.com/2014/11/23/web-api-model-binding-in-asp-net-mvc-6-asp-net-5/

原文链接:https://andrewlock.net/model-binding-json-posts-in-asp-net-core/


来源:https://blog.csdn.net/yeqiufeng/article/details/89307029
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?会员注册

×
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-26 12:51 , Processed in 0.584768 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表