0

suppose I have the following class

class A
{
  [Required]
  public string Name {get; set;}
  [Required]
  public string NickName {get; set;}
  [Required]
  public string UserId {get; set;}
}

and from the form I am passing only the Name and NickName to controller and before checking the model state simply I assign the user id to the UserId property as below

    [HttpPost]
    public IActionResult Save(A model)
    {
        model.UserId = User.GetLoggedInUserId<string>();
        if (!ModelState.IsValid)
        {
           return View(model);
        }        
    }

even though I have assign the user id before checking the model state it still returns the validation state false and complaining the for the user id. one way to come out of this problem is to create a view model which makes things more complex, because of assigning the values from view model to class it self. any idea how to solve this.

Note: the question is not only for the User Id in the class there maybe other properties as well that may not be passed from the form to controller and the values maybe assigned to them from controller

Mohammad
  • 71
  • 1
  • 10

3 Answers3

3

You could try to remove the 'UserId' from the model validation before calling ModelState.IsValid. Code like this:

    [HttpPost]
    public IActionResult CreateA(A a)
    {
        var state1 = ModelState.IsValid;  // false
        ModelState.Remove("UserId");      //using Remove method to remove the specified object from the model-state dictionary.
        var state2 = ModelState.IsValid;  // true
        
        a.UserId = "SN1001";
          
        if (ModelState.IsValid)
        {
            var data = a.UserId;
        }
        return RedirectToAction(nameof(Index));
    }

The screenshot as below:

enter image description here

Besides, you could also try to use the TryValidateModel() method to validate the model again in the controller, code like this:

    [HttpPost]
    public IActionResult CreateA(A a)
    {
        var state1 = ModelState.IsValid;  // false 
        
        ModelState.Remove("UserId");

        a.UserId = "SN1001";

        if (!TryValidateModel(a, nameof(a)))
        { 
            var state2 = ModelState.IsValid;              
        }

        if (ModelState.IsValid)
        {
            var data = a.UserId;
        }
        return RedirectToAction(nameof(Index));
    }

The result like this:

enter image description here

Reference: Model State Rerun validation

Edit

[Note] If the ModelState.IsValid is false first, before rerun validation using the TryValidateModel method, we have to remove the error from the ModelState.

Zhi Lv
  • 18,845
  • 1
  • 19
  • 30
1

You can also pass the value of UserId field using a hidden field with a default value, like:

@Html.HiddenFor(m => m.UserId, new { @Value = User.Identity.Name });

Giorgos Betsos
  • 71,379
  • 9
  • 63
  • 98
  • Nice one! Overrides the binding validation in a clever way, providing the default value. But it makes it hard if it's optional and there actually is a possible value to enter. – Athanasios Kataras Dec 16 '20 at 08:32
  • passing the user id from the form can compromise the security of the application I don't think this is a good idea – Mohammad Dec 16 '20 at 09:02
  • Then use a pseudo-value to trick the validator and override this value like you do in `Save` method. I cannot think of any other way around this. You will have to either create a ViewModel class or make the `UserId` not required. – Giorgos Betsos Dec 16 '20 at 09:05
0

This answer might help What does ModelState.IsValid do?

ModelState.IsValid indicates if it was possible to bind the incoming values from the request to the model correctly and whether any explicitly specified validation rules were broken during the model binding process.

There is no way to solve this. If you don't need this validation, then remove it altogether with the attributes and add the appropriate value handling logic with code.

The isValid will just validate the state on binding time and that's that.

Athanasios Kataras
  • 25,191
  • 4
  • 32
  • 61