【问题标题】:Model Binding not working with Attribute Routing in .net Core 2.1模型绑定不适用于 .net Core 2.1 中的属性路由
【发布时间】:2018-10-03 14:52:22
【问题描述】:

我可以让模型绑定在没有属性路由的情况下正常工作 - 例如:

/// <summary>
/// Collect user details
/// </summary>
public IActionResult RegisterDetails(Guid CustomerId)
{
    var Details = new Details()
    {
        CustomerID = CustomerId
    };

    return View(Details);
}

/// <summary>
/// Save user details in Db if valid
/// </summary>
[HttpPost]
public IActionResult RegisterDetails(Details Details)
{
    if (ModelState.IsValid)
    {
        // Do stuff
    }

    // Error, return ViewModel to view
    return View(RegisterDetails);
}

但我不确定如何将模型传递给处理它的方法。当我提交表单时,它运行原始方法,而不是 [HttpPost] 下的方法 - 它一次又一次地发布到原始方法( // Do stuff 是 - 当我这样做时:

/// <summary>
/// Collect user details
/// </summary>
[Route("Register/Details/{CustomerId}")]
public IActionResult RegisterDetails(Guid CustomerId)
{
    var Details = new Details()
    {
        CustomerID = CustomerId
    };

    return View(Details);
}

/// <summary>
/// Save user details in Db if valid
/// </summary>
[HttpPost]
[Route("Register/Details")]
public IActionResult RegisterDetails(Details Details)
{
    if (ModelState.IsValid)
    {
        // Do stuff
    }

    // Error, return ViewModel to view
    return View(RegisterDetails);
}

使用属性路由时如何正确绑定模型?

我搜索了 Google - 我发现了一些没有帮助的东西,例如: https://www.red-gate.com/simple-talk/dotnet/asp-net/improved-model-binding-asp-net-core/

谢谢

更新

我还注意到 CustomerId 被附加到 Url,即使在表单发布之后也是如此。我不认为这发生在 MVC 5 中并且不需要这个,CustomerId 隐藏在页面中。

我怎样才能删除它(它导致路由与 [HttpPost] 修饰的方法不匹配。

【问题讨论】:

  • 我对问题的措辞有点困惑。 When I submit the form it runs the original method, not the one under [HttpPost] -。在显示的两个中,哪个是原始方法?你这是什么意思
  • 非 [HttpPost] 方法 - Get 方法,代码中较高的方法。这就是 MVC 通常的工作方式,对吧……
  • 在核心中使用属性时需要明确。放上[HttpGet] 并使用路由约束避免混淆[Route("Register/Details/{CustomerId:guid}")]
  • 你不需要两者。只需执行 [HttpGet("Register/Details/{CustumerId}] 和 `[HttpPost("Register/Details")]。
  • @ChrisPratt IIRC 在文档中他们说您可以将它们用于返回视图的控制器,并且 HttpPost 本身我们更多地用于 Restful API。

标签: asp.net-mvc-5 asp.net-core-mvc attributerouting asp.net-core-mvc-2.0 asp.net-core-mvc-2.1


【解决方案1】:

通常,您可以通过 API 类控制器添加属性“路由”作为基本路径,然后添加参数。我还尝试为控制器添加动词,以便更好地理解......

[Route("Register/Details/")]
public class RegistrerExampleController : ControllerBase
{

    /// <summary>
    /// Collect user details
    /// </summary>
    [HttpGet("{CustomerId}", Name = nameof(RegisterDetails))]
    public IActionResult RegisterDetails(Guid CustomerId)
    {
        var Details = new Details()
        {
            CustomerID = CustomerId
        };

        return View(Details);
    }

    /// <summary>
    /// Save user details in Db if valid
    /// </summary>
    [HttpPost]
    public IActionResult RegisterDetails(Details Details)
    {
        if (ModelState.IsValid)
        {
            // Do stuff
        }

        // Error, return ViewModel to view
        return View(RegisterDetails);
    }
}

你试过了吗?

【讨论】:

    【解决方案2】:

    问题有两个方面。首先,你有你的RegisterDetails(Guid CustomerId) 动作,通过Route 属性设置路由。这将打开它以接受所有 HTTP 动词:GET、POST 等。其次,虽然您没有发布您的视图,但我可以假设您可能有一个带有空操作的表单,即:

     <form method="post">
         ...
     </form>
    

    这将默认回发到同一页面,在这种情况下为/Register/Details/{CustomerId}。由于该路由与您明确标记为 HttpPost 的操作不匹配并且它匹配的操作接受 POST(因为使用了 Route),因此再次调用采用客户 ID 的操作。由于没有代码可以实际处理其中的帖子,因此它只是重新加载您的页面。

    首先,您应该在操作上使用显式 HTTP 动词属性而不是 Route,即:

    [HttpGet("Register/Details/{CustomerId}")]
    public IActionResult RegisterDetails(Guid CustomerId)
    
    [HttpPost("Register/Details")]
    public IActionResult RegisterDetails(Details Details)
    

    然后,如果你想保留这个路由方案,你需要明确你的表单。但是,由于这两个动作具有相同的名称,所以这有点难以做到。 ASP.NET Core 将尝试回填路由的 CustomerId 部分,因为它已存在于路由数据中。您有三个选择:

    1. 使用不同的动作名称。然后,您可以将表单显式绑定到您的发布操作:

      [HttpPost("Register/Details")]
      public IActionResult RegisterDetailsPost(Details Details)
      

      然后:

      <form asp-action="RegisterDetailsPost" method="post">
      
    2. 使用路线名称:

      [HttpPost("Register/Details", Name = "RegisterDetailsPost")]
      public IActionResult RegisterDetails(Details Details)
      

      然后:

      <form asp-route="RegisterDetailsPost" method="post">
      
    3. 不要将客户 ID 作为初始路由的一部分。如果这两个路由确实匹配,那么将根据正在播放的 HTTP 动词拉取正确的路由。换句话说:

      [HttpGet("Register/Details")]
      public IActionResult RegisterDetails(Guid CustomerId)
      

      您仍然可以提供客户 ID,但您需要使用查询字符串,即 /Register/Details?CustomerId=123456。在您的链接中生成 URL 等也没有什么不同。 ASP.NET Core 将自动附加包含的任何路由数据,这些数据不是明确的路由参数作为查询字符串参数。换句话说,您仍然可以执行以下操作:

      <a asp-action="RegisterDetails" asp-route-customerId="123456">Click me</a>
      

    【讨论】:

    • 谢谢 - 我似乎遇到的一个附带问题是,即使在表单发布后,实际的 customerId 也被附加到 Url。这看起来很奇怪——你知道为什么会这样吗? (如果我将“/{CustomerId}”添加到 HttpPost 路由的末尾,它可以工作 - 没有它它不会)。
    • 我实际上不需要在表单帖子的 URL 中传递值 - 它隐藏在表单中。
    【解决方案3】:

    尝试使用 FromBody 属性。

    public IActionResult RegisterDetails([FromBody]Details Details)
    

    【讨论】:

      猜你喜欢
      • 2017-10-07
      • 1970-01-01
      • 2020-07-24
      • 1970-01-01
      • 1970-01-01
      • 2021-08-18
      • 2017-12-05
      • 1970-01-01
      • 2022-06-11
      相关资源
      最近更新 更多