Home > front end >  Controller failing to bind property
Controller failing to bind property

Time:11-10

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,

  1. A Get method to view the page with an empty model
  2. Prepare the form using the model
  3. 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>
  • Related