模型绑定不适用于 .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 部分,因为它已经存在于路由数据中。您有三个选择:
使用不同的动作名称。然后,您可以明确地将您的表单绑定到您的 post 操作:
[HttpPost("Register/Details")]
public IActionResult RegisterDetailsPost(Details Details)
然后:
<form asp-action="RegisterDetailsPost" method="post">
雇佣路线名称:
[HttpPost("Register/Details", Name = "RegisterDetailsPost")]
public IActionResult RegisterDetails(Details Details)
然后:
<form asp-route="RegisterDetailsPost" method="post">
不要将客户 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)
我可以让模型绑定在没有属性路由的情况下正常工作 - 例如:
/// <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 部分,因为它已经存在于路由数据中。您有三个选择:
使用不同的动作名称。然后,您可以明确地将您的表单绑定到您的 post 操作:
[HttpPost("Register/Details")] public IActionResult RegisterDetailsPost(Details Details)
然后:
<form asp-action="RegisterDetailsPost" method="post">
雇佣路线名称:
[HttpPost("Register/Details", Name = "RegisterDetailsPost")] public IActionResult RegisterDetails(Details Details)
然后:
<form asp-route="RegisterDetailsPost" method="post">
不要将客户 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)