.NET Core 2.1中的模型绑定不适用于属性路由

时间:2018-10-03 14:52:23

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

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

/// <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]下的方法-它会一遍又一遍地发布到原始方法(其中//做事是-当我这样做时:

/// <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/

thx

更新

我还注意到,即使在表单发布之后,CustomerId也会附加到Url。我认为这不是在MVC 5中发生的,也不是必须的,CustomerId隐藏在页面中。

如何删除此路由(导致路由与[HttpPost]装饰的方法不匹配。

3 个答案:

答案 0 :(得分:1)

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

[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);
    }
}

您尝试过吗?

答案 1 :(得分:1)

这个问题有两个方面。首先,您要执行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>
    

答案 2 :(得分:0)

尝试使用FromBody属性。

public IActionResult RegisterDetails([FromBody]Details Details)