Modelstate.Isvalid Even When It Should Not Be

ModelState.IsValid even when it should not be?

The ModelState.IsValid internally checks the Values.All(modelState => modelState.Errors.Count == 0) expression.

Because there was no input the Values collection will be empty so ModelState.IsValid will be true.

So you need to explicitly handle this case with:

if (user != null && ModelState.IsValid)
{

}

Whether this is a good or bad design decision that if you validate nothing it will true is a different question...

Does ModelState.IsValid=true guarantee that the passed model parameter is not null?

To answer your question, no, ModelState.IsValid does not check if your model is null and will throw an error if that happens.

In an API it is quite easy to have null models, if you make a mistake while building your request model and it doesn't match what the endpoint expects.

Or someone else could look at your website, see the API calls and decides to have some fun and flood your API with requests which don't have valid models.

There are ways to check for null models in one place such as here : ModelState is valid with null model

Model Validation is working even without ModelState.IsValid netcore3

Nevermind, just saw this https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-3.0

Web API controllers don't have to check ModelState.IsValid if they have the [ApiController] attribute. In that case, an automatic HTTP 400 response containing issue details is returned when model state is invalid. For more information, see Automatic HTTP 400 responses.

Model validation - Why ModelState.IsValid always returns true even when no values are supplied?

I don't know why your model is valid if you don't supply gender, but you can make this value not have a default value by defining the Gender value as nullable, as follows:

public class Request
{
public int id { get; set; }

public string Name { get; set; }

[Required]
public Gender? Gender { get; set; }
}

Alternatively you can specify a default value for gender, as follows:

public enum Gender
{
Unknown = 0,
Male,
Female
}

Update
I now can see the difference between our results, again using Postman, if I submit a raw request as xml:

Header: Content-Type text/xml

<Request xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://schemas.datacontract.org/2004/07/webapi1.Models">
<id>1</id>
<Name>testName</Name>
</Request>

My model is valid, however if I submit x-www-form-urlencoded data:

Header: Content-Type application/x-www-form-urlencoded

id=1,Name=testname

Then my model is invalid, even though the value type has a value, my modelstate tells me that the Gender property is required.

As x-www-form-urlencoded is part of the query string then I guess that MVC is able to determine that the value was missing, but when the data is submitted as plain xml it can't determine this.

I suggest that if you want the required attribute to work in all cases, you make your value types nullable as follows:

[Required]
public Gender? Gender { get; set; }

ModelState.IsValid() does not validate even after the previous ModelState was closed

The second request to public IActionResult Index(userModel model) whose model doesn't contais email. IsValid is false.

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

The ModelState instance for the email address has an error in the Errors collection.

Sample Image

When MVC creates the model state for the submitted properties, it also goes through each property in the ViewModel and validates the property using attributes associated to it. If any errors are found, they are added to the Errors collection in the property's ModelState.

Solution

Just reomve if (ModelState.IsValid) in Index action and there is no need to add it at all.

or

if you insist to keep if (ModelState.IsValid), you need to add email inside form.

    <input type="hidden" asp-for="email" />

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.

In your example, the model that is being bound is of class type Encaissement. Validation rules are those specified on the model by the use of attributes, logic and errors added within the IValidatableObject's Validate() method - or simply within the code of the action method.

The IsValid property will be true if the values were able to bind correctly to the model AND no validation rules were broken in the process.

Here's an example of how a validation attribute and IValidatableObject might be implemented on your model class:

public class Encaissement : IValidatableObject
{
// A required attribute, validates that this value was submitted
[Required(ErrorMessage = "The Encaissment ID must be submitted")]
public int EncaissementID { get; set; }

public DateTime? DateEncaissement { get; set; }

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();

// Validate the DateEncaissment
if (!this.DateEncaissement.HasValue)
{
results.Add(new ValidationResult("The DateEncaissement must be set", new string[] { "DateEncaissement" });
}

return results;
}
}

Here's an example of how the same validation rule may be applied within the action method of your example:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "EncaissementID,libelle,DateEncaissement,Montant,ProjetID,Description")] Encaissement encaissement) {

// Perform validation
if (!encaissement.DateEncaissement.HasValue)
{
this.ModelState.AddModelError("DateEncaissement", "The DateEncaissement must be set");
}

encaissement.Montant = Convert.ToDecimal(encaissement.Montant);

ViewBag.montant = encaissement.Montant;

if (ModelState.IsValid) {

db.Encaissements.Add(encaissement);
db.SaveChanges();
return RedirectToAction("Index", "Encaissement");

};

ViewBag.ProjetID = new SelectList(db.Projets, "ProjetId", "nomP");

return View(encaissement);
}

It's worth bearing in mind that the value types of the properties of your model will also be validated. For example, you can't assign a string value to an int property. If you do, it won't be bound and the error will be added to your ModelState too.

In your example, the EncaissementID value could not have a value of "Hello" posted to it, this would cause a model validation error to be added and IsValid will be false.

It is for any of the above reasons (and possibly more) that the IsValid bool value of the model state will be false.

ModelState.IsValid Return False Even There is No Error Why?

Look at your view and model again, you have put required on all your properties within the class, however you are returning 7 of 9 properties. then Model.IsValid will look at your Model and see oh look there 2 required properties that you are not returning, so it will be false. either remove [required] on those properties or add them within your view. good luck.

Update
Those 2 Properties are:

UserName And Password, they need to be within your Html.BeginForm, so they will be send to server as the part of your class.

ModelState.IsValid returns False even if the required property is valid

    public IActionResult Login()
{
return View(new User());
}

[HttpPost]
public async Task<IActionResult> Login(User model,string returnURL = null)
{
if (ModelState.IsValid)
{
_logger.LogInformation(model.ToString());
}
return View(model);
}

Whay cannot hit the breakpoint in Controller when ModelState.IsValid is false?

ASP.Net Core has a default ModelStateInvalidFilter which, being a filter, is being executed before the controller method is executed. When the ModelState.Valid == false none of your code in the controller will be executed. However, you can suppress the filter:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
}

Keep in mind that this disables the filter globally. If you want to be able to disable it for specific actions, see https://stackoverflow.com/a/56350823/12431728

Which suggests a custom filter which looks like this:

/// <summary>
/// Suppresses the default ApiController behaviour of automatically creating error 400 responses
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class SuppressModelStateInvalidFilterAttribute : Attribute, IActionModelConvention {
private static readonly Type ModelStateInvalidFilterFactory = typeof(ModelStateInvalidFilter).Assembly.GetType("Microsoft.AspNetCore.Mvc.Infrastructure.ModelStateInvalidFilterFactory");

public void Apply(ActionModel action) {
for (var i = 0; i < action.Filters.Count; i++) {
if (action.Filters[i] is ModelStateInvalidFilter || action.Filters[i].GetType() == ModelStateInvalidFilterFactory) {
action.Filters.RemoveAt(i);
break;
}
}
}
}

To use the filter just add [SuppressModelStateInvalidFilter] above the desired controller action method.



Related Topics



Leave a reply



Submit