-2

I cannot work out why does the Id property of my model not get validated by the modelstate.

This happens in a Create action, where the Id doesn't get returned from the view to the model, as it is only about to be generated.

The bottom line is, naturally, a desirable outcome, as validating a missing Id would only cause problems, but I cannot find any instructions which would tell modelstate to skip this property and I'd be keen to understand it better for later reuse.

I will also add, that all the properties of this model are marked as required.

EDIT: The indicated duplicate, points to a different problem.

What I am tryign to point at here, is that the ID attribute, does not go through validation at all, and not the fact that it passes the validation. Please see the modelstate pop-up box in attached screenshot, the modelstate does not look at the Id property at all.

Here's how it looks like in debugging: enter image description here

The screenshot controllers is this:

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "Id,Name,ClientName")] Project project)
    {
        if (ModelState.IsValid)
        {
            //db.Entry(project).State = EntityState.Modified;
            db.MarkAsModified(project);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(project);

My model is this:

public class Project
{
    [Required]
    public int Id { get; set; }

    [Required]
    [Display(Name = "Project name")]
    public string Name { get; set; }

    [Required]
    public string ClientName { get; set; }
}

And the view that returns the user input is this:

@model Freelancer_DeveloperChallenge.Models.Project

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>


@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>Project</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.ClientName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ClientName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.ClientName, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
JKJ
  • 535
  • 4
  • 14
  • Please include a [mcve], not a picture of code. – Heretic Monkey Jul 21 '17 at 13:59
  • 3
    `[Required]` means "not null". `0` is not null. – CodeCaster Jul 21 '17 at 14:02
  • 1
    make your id nullable – jamiedanq Jul 21 '17 at 14:02
  • What I am tryign to point at here, is that the ID attribute, does not go through validation at all, and not the fact that it passes this validation. Please look at the modelstate box. It does not contain the ID property at all. – JKJ Jul 21 '17 at 14:06
  • This is just another reason why you ALWAYS use view models, and your `Id` property would be `int?` (nullable) with the `[Required]` attribute so that your protected against under-posting attacks (refer [this answer](https://stackoverflow.com/questions/43688968/what-does-it-mean-for-a-property-to-be-required-and-nullable/43689575#43689575) for more detail) –  Jul 22 '17 at 00:18
  • Stephen, I reviewed your link, but I do not believe that your answer there is accrurate. Having a viemodel with a nullable property will not automatically prevent under-posting attacks purely by the use of modelstate validation. You will need to make additional ifchecks to confirm binded property values are not null. Like on my example here, values which do not get sent throug with the http post request, do not go through modelstate validation at all, regardless of whether they are nullable or not. The [required] attribute doesn't make a difference. Refer to the msdn documentation below. – JKJ Jul 22 '17 at 23:35
  • It is correct. By adding the `[Required]` attribute and making it nullable, validation will be performed and you will get a `ModelState` error if the request does not contain a name/value pair for the property. And read the link in that answer. –  Jul 23 '17 at 02:46
  • Indeed, I stand corrected. Thanks for the reply. This is quite a complex mechanism if anybody gets here I will refer to the same link: http://bradwilson.typepad.com/blog/2010/01/input-validation-vs-model-validation-in-aspnet-mvc.html – JKJ Jul 23 '17 at 11:33

1 Answers1

1

This is the expected result here is why:

  • Your Id field is a non-nullable int.

  • The default value of a non-nullable int as you know is 0.

  • Per the Microsoft documentation:

The RequiredAttribute attribute specifies that when a field on a form is validated, the field must contain a value. A validation exception is raised if the property is null, contains an empty string (""), or contains only white-space characters.

Source: https://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.requiredattribute.aspx

EDIT: Here is additional info from Microsoft's docs to answer the question that you posted in your comment:

Model state represents validation errors in submitted HTML form values.

Model validation occurs prior to each controller action being invoked, and it is the action method’s responsibility to inspect ModelState.IsValid and react appropriately. In many cases, the appropriate reaction is to return some kind of error response, ideally detailing the reason why model validation failed.

Source: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation

So as mentioned in the documentation:

  • Only values submitted from your form are validated (Id wasn't submitted from your form and even if it was it will always pass validation since it's a non-nullable int).

  • Model validation occurs prior to each controller action being invoked.

Community
  • 1
  • 1
univ
  • 717
  • 4
  • 12
  • Thanks Univ, I am however in doubt, whether this approach describes the situation. The key here is, that the Id field does not get validated at all, as it is not part of the modelstate object. To reiterate, the Id property does not go through validation at all, it doesn't have either a false nor a true state, as it isn't one of modelstate key,value pairs. Even though, the parameter variable, upon construction, has the Id property set to 0 (because that is the default for Int), the 0 is set there by the constructor because it is not recieved from the view. – JKJ Jul 21 '17 at 17:53
  • Thanks, thats a good explanation there in that doc, that the validation will only be applied to items submitted by the form. Since the view doesn't send that property, it does not go through validation at all. – JKJ Jul 21 '17 at 19:24