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

Model Binding not working with Attribute Routing in .net Core 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] 下的方法 - 它一次又一次地发布到原始方法(// 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] 装饰方法不匹配。

通常情况下,您可以通过API Class控制器添加属性"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);
    }
}

你试过吗?

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

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

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

首先,您应该在操作中使用明确的 HTTP 动词属性而不是 Route,即:

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

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

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

  1. 使用不同的动作名称。然后,您可以明确地将您的表单绑定到您的 post 操作:

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

尝试使用 FromBody 属性。

public IActionResult RegisterDetails([FromBody]Details Details)