Привязка модели не работает с маршрутизацией атрибутов в.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] - он продолжает публиковать в исходном методе снова и снова (где // Делать вещи - когда я делаю это:
/// <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].
3 ответа
Как правило, вы можете добавить атрибут "маршрут" через контроллер класса 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);
}
}
Вы пробовали это?
Вопрос в два раза. Во-первых, у вас есть RegisterDetails(Guid CustomerId)
действие с маршрутом, установленным через Route
приписывать. Это открывает его для принятия всех HTTP-глаголов: GET, POST и т. Д. Во-вторых, хотя вы не опубликовали свое представление, я могу предположить, что у вас, вероятно, есть форма с пустым действием, т.е.
<form method="post">
...
</form>
По умолчанию будет отправляться обратно на ту же страницу, что здесь в этом сценарии /Register/Details/{CustomerId}
, Поскольку этот маршрут не соответствует вашим действиям, явно помеченным как HttpPost
и действие, которое оно соответствует, принимает POST (из-за использования Route
), действие, которое принимает идентификатор клиента, вызывается снова. Поскольку в нем нет кода для обработки сообщения, он просто перезагружает вашу страницу.
Прежде всего, вы должны использовать явные атрибуты глагола HTTP, а не Route
на ваши действия, а именно:
[HttpGet("Register/Details/{CustomerId}")]
public IActionResult RegisterDetails(Guid CustomerId)
[HttpPost("Register/Details")]
public IActionResult RegisterDetails(Details Details)
Затем, если вы хотите сохранить эту схему маршрутов, вам нужно четко указать форму. Однако, поскольку оба действия имеют одно и то же имя, это немного сложно сделать. ASP.NET Core попытается выполнить обратную засыпку части CustomerId маршрута, поскольку она уже существует в данных маршрута. У вас есть три варианта:
Используйте разные имена действий. Затем вы можете явно привязать свою форму к действию публикации:
[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">
Не делайте идентификатор клиента частью начального маршрута. Если два маршрута действительно совпадают, то будет выбран правильный путь, основанный на действующем глаголе HTTP. Другими словами:
[HttpGet("Register/Details")] public IActionResult RegisterDetails(Guid CustomerId)
Вы все еще можете указать идентификатор клиента, но вам нужно использовать строку запроса, т.е.
/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)