My controller is failing to bind properties on form submission.
Here's the controller
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult RegisterUser(RegisterInputModel inputModel)
{
_logger.LogInformation($"email: {inputModel.Email}");
return Redirect("/");
}
Here's the form:
<form method="post" asp-route="RegisterUser">
<h4>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="RegisterInput.Email"></label>
<input asp-for="RegisterInput.Email" class="form-control" autofocus />
<span asp-validation-for="RegisterInput.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="RegisterInput.Password"></label>
<input asp-for="RegisterInput.Password" class="form-control" />
<span asp-validation-for="RegisterInput.Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="RegisterInput.ConfirmPassword"></label>
<input asp-for="RegisterInput.ConfirmPassword" class="form-control" />
<span asp-validation-for="RegisterInput.ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
public class RegisterInputModel
{
Required]
EmailAddress]
Display(Name = "Email")]
public string Email { get; set; }
Required]
StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters
long.", MinimumLength = 6)]
DataType(DataType.Password)]
public string Password { get; set; }
DataType(DataType.Password)]
Display(Name = "Confirm password")]
Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
After debugging the submitted data does come through the Request
property of the controller but the binding fails.
CodePudding user response:
Firstly, if you use asp.net core mvc, you need change asp-route="RegisterUser"
to asp-action="RegisterUser"
.
Secondly, model binding system looks through the source by name and asp-for
will generate the id and name in html. So you can see that your name here is something like:RegisterInput.PropertyName
, it does not match the backend property name. You need add [Bind(Prefix = "RegisterInput")]
to specific the prefix of the name or you can change the RegisterInputModel inputModel
to RegisterInputModel RegisterInput
.
Here is the whole working demo:
Model:
public class YourModel
{
public RegisterInputModel RegisterInput { get; set; }
}
public class RegisterInputModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
View(Index.cshtml):
@model YourModel
<form method="post" asp-action="RegisterUser"> //change here...
<h4>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" ></div>
<div >
<label asp-for="RegisterInput.Email"></label>
<input asp-for="RegisterInput.Email" autofocus />
<span asp-validation-for="RegisterInput.Email" ></span>
</div>
<div >
<label asp-for="RegisterInput.Password"></label>
<input asp-for="RegisterInput.Password" />
<span asp-validation-for="RegisterInput.Password" ></span>
</div>
<div >
<label asp-for="RegisterInput.ConfirmPassword"></label>
<input asp-for="RegisterInput.ConfirmPassword" />
<span asp-validation-for="RegisterInput.ConfirmPassword" ></span>
</div>
<button type="submit" >Register</button>
</form>
Controller:
public class HomeController : Controller
{
//render Index.cshtml view...
public async Task<IActionResult> Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult RegisterUser([Bind(Prefix = "RegisterInput")]
RegisterInputModel inputModel)
//public IActionResult RegisterUser(RegisterInputModel RegisterInput)
{
return Redirect("/");
}
}
Startup.cs:
public class Startup
{
//...
public void ConfigureServices(IServiceCollection services)
{
//....
services.AddControllersWithViews()
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
CodePudding user response:
try to add AntiForgeryToken just below a form tag, or remove it from a controller action
<form method="post" asp-route="RegisterUser">
@Html.AntiForgeryToken()
CodePudding user response:
For ASP.Net Core Controller/View (not Razor page), I usually use the below approach,
- A Get method to view the page with an empty model
- Prepare the form using the model
- Submit the form
You can check the below code as a reference
Controller:
[HttpGet]
public IActionResult RegisterUser()
{
var model = new RegisterInputModel();
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult RegisterUser(RegisterInputModel inputModel)
{
_logger.LogInformation($"email: {inputModel.Email}");
return Redirect("/");
}
View:
@model RegisterInput
<form method="post">
<h4>Create a new account.</h4>
<hr />
<div asp-validation-summary="All" ></div>
<div >
<label asp-for="Email"></label>
<input asp-for="Email" autofocus />
<span asp-validation-for="Email" ></span>
</div>
<div >
<label asp-for="Password"></label>
<input asp-for="Password" />
<span asp-validation-for="Password" ></span>
</div>
<div >
<label asp-for="ConfirmPassword"></label>
<input asp-for="ConfirmPassword" />
<span asp-validation-for="ConfirmPassword" ></span>
</div>
<button type="submit" >Register</button>
</form>