首页 > 解决方案 > 无法在 POST 调用中接收来自 Route 和 Body 的值

问题描述

在 Asp.Net Core 5.0 项目中,我有ApiController路线和模型:

[HttpPost("users/{userId:int}/confirm-email")]
public async Task<IActionResult> ConfirmEmail(Model model) { }

public class Model { 
  [FromRoute] public Int32? UserId { get; set; }
  [FromQuery] public String Token { get; set; } 
}

结果:我得到了我所期望的......

UserId = 10
Token = "ABC"

我尝试在正文中而不是在查询参数中接收令牌:

public class Model { 
  [FromRoute] public Int32? UserId { get; set; }
  [FromBody] public String Token { get; set; } 
}

在这种情况下,我得到了错误:

"errors": {
  "$": [
    "The JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
  ]
}

我使用 Insomnia.Rest 调用 API,正文为:

{
  "token": "ABC"
}

然后我尝试添加[FromBody]到控制器操作:

[HttpPost("users/{userId:int}/confirm-email")]
public async Task<IActionResult> ConfirmEmail([FromBody]Model model) { }

public class Model { 
  [FromRoute] public Int32? UserId { get; set; }
  [FromBody] public String Token { get; set; } 
}

结果:我从正文中获得了令牌,但现在 userId 为空。

UserId = null
Token = "ABC" 

我一直在尝试一些变化,但我无法弄清楚发生了什么。

我也尝试从 Angular 调用 API,结果是一样的。

知道我错过了什么吗?

标签: asp.net-coreasp.net-core-webapiasp.net-core-5.0

解决方案


默认情况下,模型绑定从 HTTP 请求中的以下来源以键值对的形式获取数据:

  • 表单域
  • 请求正文(对于具有 [ApiController] 属性的控制器。)
  • 路线数据
  • 查询字符串参数
  • 上传的文件

对于每个目标参数或属性,源将按照前面列表中指示的顺序进行扫描。

您可以参考以下屏幕截图:

在此处输入图像描述

我们可以看到,应用于属性的[FromRoute][FromForm]属性不起作用。它使用默认的推理规则来绑定模型。

然后,当[FromBody]属性应用于复杂类型参数(将[FromBody]属性添加到 Controller 操作)时,将忽略应用于其属性的任何绑定源属性。所以,UserId为空。更多详细信息,请参阅[FromBody]属性绑定源参数推断

因此,在这种情况下,要从路由和请求正文中访问值,最简单的方法是在ConfirmEmail方法中添加一个参数,代码如下:

    [HttpPost("users/{userId:int}/confirm-email")]
    public async Task<IActionResult> ConfirmEmail(Model model, [FromRoute]int userid)
    {
        //set the userid
        model.userId = userid;

        return Ok("success");
    }

结果如下:

在此处输入图像描述

此外,您还可以尝试创建自定义模型绑定器来绑定模型。

这里有一些相关的文章,你可以参考它们:

ASP.NET Core 中的自定义模型绑定

ASP.NET Core MVC 中的自定义模型绑定


推荐阅读