Get error message if ModelState.IsValid fails?
Asked Answered
M

14

108

I have this function in my controller.

[HttpPost]
public ActionResult Edit(EmployeesViewModel viewModel)
{
    Employee employee = GetEmployee(viewModel.EmployeeId);
    TryUpdateModel(employee);

    if (ModelState.IsValid)
    {
        SaveEmployee(employee);
        TempData["message"] = "Employee has been saved.";
        return RedirectToAction("Details", new { id = employee.EmployeeID });
    }

    return View(viewModel); // validation error, so redisplay same view
}

It keeps failing, ModelState.IsValid keeps returning false and redisplaying the view. But I have no idea what the error is.

Is there a way to get the error and redisplay it to the user?

Monochromat answered 6/3, 2011 at 17:50 Comment(2)
it has been solved before #1353448Yarak
Does this answer your question? How to get all Errors from ASP.Net MVC modelState?Soriano
C
43

You can do this in your view without doing anything special in your action by using Html.ValidationSummary() to show all error messages, or Html.ValidationMessageFor() to show a message for a specific property of the model.

If you still need to see the errors from within your action or controller, see the ModelState.Errors property

Corder answered 6/3, 2011 at 17:54 Comment(3)
There is no ModelState.Errors property?!Superable
@Superable I think he means the property of the type `ModelState", whereas the Controller.ModelState property is of type ModelStateDictionary.Frederiksen
@Superable ModelState.Errors only exists for MVC, not WebAPILayton
E
175

Try this

if (ModelState.IsValid)
{
    //go on as normal
}
else
{
    var errors = ModelState.Select(x => x.Value.Errors)
                           .Where(y=>y.Count>0)
                           .ToList();
}

errors will be a list of all the errors.

If you want to display the errors to the user, all you have to do is return the model to the view and if you haven't removed the Razor @Html.ValidationFor() expressions, it will show up.

if (ModelState.IsValid)
{
    //go on as normal
}
else
{
    return View(model);
}

The view will show any validation errors next to each field and/or in the ValidationSummary if it's present.

Emmet answered 20/12, 2012 at 8:35 Comment(0)
G
112

If you're looking to generate a single error message string that contains the ModelState error messages you can use SelectMany to flatten the errors into a single list:

if (!ModelState.IsValid)
{
    var message = string.Join(" | ", ModelState.Values
        .SelectMany(v => v.Errors)
        .Select(e => e.ErrorMessage));
    return new HttpStatusCodeResult(HttpStatusCode.BadRequest, message);
}
Gonococcus answered 12/6, 2014 at 15:58 Comment(2)
Sometimes the ErrorMessage is not provided, e.g. if a required DateTime field is not set. In this case, look for the exception message, e.g. e.Exception.Message.Somite
Sometimes that's not provided either! I've got a ModelState with 5 Errors each with a null Exception and empty string for the ErrorMessage whilst other entries on the ModelState simply have no Errors associated with them.Earley
C
43

You can do this in your view without doing anything special in your action by using Html.ValidationSummary() to show all error messages, or Html.ValidationMessageFor() to show a message for a specific property of the model.

If you still need to see the errors from within your action or controller, see the ModelState.Errors property

Corder answered 6/3, 2011 at 17:54 Comment(3)
There is no ModelState.Errors property?!Superable
@Superable I think he means the property of the type `ModelState", whereas the Controller.ModelState property is of type ModelStateDictionary.Frederiksen
@Superable ModelState.Errors only exists for MVC, not WebAPILayton
T
9

If Modal State is not Valid & the error cannot be seen on screen because your control is in collapsed accordion, then you can return the HttpStatusCode so that the actual error message is shown if you do F12. Also you can log this error to ELMAH error log. Below is the code

if (!ModelState.IsValid)
{
              var message = string.Join(" | ", ModelState.Values
                                            .SelectMany(v => v.Errors)
                                            .Select(e => e.ErrorMessage));

                //Log This exception to ELMAH:
                Exception exception = new Exception(message.ToString());
                Elmah.ErrorSignal.FromCurrentContext().Raise(exception);

                //Return Status Code:
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest, message);
}

But please note that this code will log all validation errors. So this should be used only when such situation arises where you cannot see the errors on screen.

Tailpipe answered 25/2, 2016 at 12:49 Comment(1)
Would it be possible to get name/message pairs so we know which field had the error?Kalpa
A
6

If anyone is here for WebApi (not MVC) you just return the ModelState object:

return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);

Angeloangelology answered 14/2, 2019 at 0:23 Comment(0)
N
4

Here's a useful snippet I can copy/paste into my OnPostASync() handler to quickly identify the failing validation item(s):

public async Task<IActionResult> OnPostAsync(string editButton)
    {
        if (!ModelState.IsValid)
        {
            var errors = 
                from value in ModelState.Values
                where value.ValidationState == ModelValidationState.Invalid
                select value;  
            return Page();  // <-- I set a breakpoint here, and examine "errors"
        }
        ...

Unlike some of the other solutions above, I can see both

  1. The error message text (ErrorMessage) ... AND ...
  2. The specific field that caused the error
Nostoc answered 2/3, 2021 at 5:48 Comment(1)
If you prefer the method syntax: var errors = ModelState.Values.Where(value => value.ValidationState == ModelValidationState.Invalid); The Select is redundant, as Where will return the matching selection.Comply
N
3

It is sample extension

public class GetModelErrors
{
    //Usage return Json to View :
    //return Json(new { state = false, message = new GetModelErrors(ModelState).MessagesWithKeys() });
    public class KeyMessages
    {
        public string Key { get; set; }
        public string Message { get; set; }
    }
    private readonly ModelStateDictionary _entry;
    public GetModelErrors(ModelStateDictionary entry)
    {
        _entry = entry;
    }

    public int Count()
    {
        return _entry.ErrorCount;
    }
    public string Exceptions(string sp = "\n")
    {
        return string.Join(sp, _entry.Values
            .SelectMany(v => v.Errors)
            .Select(e => e.Exception));
    }
    public string Messages(string sp = "\n")
    {
        string msg = string.Empty;
        foreach (var item in _entry)
        {
            if (item.Value.ValidationState == ModelValidationState.Invalid)
            {
                msg += string.Join(sp, string.Join(",", item.Value.Errors.Select(i => i.ErrorMessage)));
            }
        }
        return msg;
    }

    public List<KeyMessages> MessagesWithKeys(string sp = "<p> ● ")
    {
        List<KeyMessages> list = new List<KeyMessages>();
        foreach (var item in _entry)
        {
            if (item.Value.ValidationState == ModelValidationState.Invalid)
            {
                list.Add(new KeyMessages
                {
                    Key = item.Key,
                    Message = string.Join(null, item.Value.Errors.Select(i => sp + i.ErrorMessage))
                });
            }
        }
        return list;
    }
}
Nominalism answered 3/9, 2018 at 16:4 Comment(0)
I
3
ModelState.Values.SelectMany(v => v.Errors).ToList().ForEach(x => _logger.Error($"{x.ErrorMessage}\n"));
Interdigitate answered 6/6, 2019 at 16:15 Comment(0)
A
3

i use this method as a generic way to get errors

 public static List<string> GetModelErrros(ModelStateDictionary modelState)
        {
            var errors = modelState.Values.Where(E => E.Errors.Count > 0)
                         .SelectMany(E => E.Errors)
                         .Select(E => E.ErrorMessage)
                         .ToList();

            return errors;
        }

and you may use it

logger.LogInformation("ID {0} input Error  ", string.Join(",", SiteUtils.GetModelErrros(ModelState)));
Actium answered 17/6, 2021 at 15:29 Comment(0)
V
2

Ok Check and Add to Watch:

  1. Do a breakpoint at your ModelState line in your Code
  2. Add your model state to your Watch
  3. Expand ModelState "Values"
  4. Expand Values "Results View"

Now you can see a list of all SubKey with its validation state at end of value.

So search for the Invalid value.

Volcano answered 29/4, 2017 at 18:50 Comment(0)
T
2

I like creating a ModelState extension method

public static class ModelStateExtensions
{

    public static BadRequestObjectResult As<BadRequestObjectResult>(this ModelStateDictionary modelState) 
    {
        var message = string.Join("\r\n", modelState.Values
                                           .SelectMany(v => v.Errors)
                                           .Select(e => e.ErrorMessage));
        return (BadRequestObjectResult)Activator.CreateInstance(typeof(BadRequestObjectResult), message)!;
    }
}

Then use it like

        if (!ModelState.IsValid)
        {
            return ModelState.As<BadRequestObjectResult>();
        }
Tyrothricin answered 21/7, 2022 at 19:48 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Institutor
M
0
publicIHttpActionResultPost(Productproduct) {  
    if (ModelState.IsValid) {  
        //Dosomethingwiththeproduct(notshown).  
        returnOk();  
    } else {  
        returnBadRequest();  
    }  
}

OR

public HttpResponseMessage Post(Product product)
        {
            if (ModelState.IsValid)
            {
                // Do something with the product (not shown).

                return new HttpResponseMessage(HttpStatusCode.OK);
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }
        }
Mercier answered 20/6, 2020 at 12:28 Comment(0)
P
-1

I have no idea if this is your problem, but if you add a user and then change the name of your application, that user will remain in the database (of course), but will be invalid (which is correct behavior). However, there will be no error added for this type of failure. The error list is empty, but ModelState.IsValid will return false for the login.

Platino answered 20/11, 2011 at 23:30 Comment(0)
G
-1

Try

ModelState.Values.First().Errors[0].ErrorMessage
Goodish answered 13/2, 2019 at 16:15 Comment(1)
This gets only first fields first error. try look at @johnnyHK answer above.Dior

© 2022 - 2024 — McMap. All rights reserved.