1

I have looked at a number of posts on this topic - custom server side validation - here and here and here (I believe this is for MVC4) but none seem to address this for me.

I have created a basic MVC6 project (two textboxes) just to test dataannotations and still cannot get this to work. I am using, as a basis, this tutorial and have recreated his data validation class with no changes.

I am not using the actual model but rather a viewModel that if validation is successful updates the database through assignment to the model.

I am not interested in a successful validation here but whether, upon a "(ModelState.IsValid)" equal to "False", it displays the error message under the textbox.

I have stepped through it and find that it does step through the actual custom validator and it certainly gives a model state of false on the custom validation - returns the view - but no error message displays.

If, however, I remove everything from the textbox - make it empty - the viewmodel is returned but this time it comesup with the "Required" error in red.. that is, the error messages work for data annotations just not custom annotations.

OK so why does it display normal annotation validation errors and not the custom validation errors?

Is this as a result of ASP.NET core or is it just the way I am returning the viewmodel (eg an error by me much more likely)?

I have decided to include all the moving parts as it might be any one of them not being correct or at issue. So thats a model, a viewmodel based on the model, a controller and the custom validator class as per the tutorial.

    public class CompanyDetail
    {
         public int CompanyDetailId { get; set; }
         public string CompanyName { get; set; }
         public string ABN { get; set; }
    }

A CompanyDetailViewModel with the data annotations added:

 public class CompanyDetailsViewModel
{
    public int CompanyDetailsId { get; set; }

    [ExcludeChar("/")]
    [Required(ErrorMessage = "A Company Name is required")]
    [Display(Name = "Company Name:")]
    [StringLength(100)]
    public string CompanyName { get; set; }

    [Required(ErrorMessage = "An ABN is required")]
    [CheckValidABN(ErrorMessage = "This is not a valid ABN")]
    [Display(Name = "ABN:")]
    public string ABN { get; set; }
}

A controller: public class CompanyDetailsController : Controller { private ApplicationDbContext _context;

    public CompanyDetailsController(ApplicationDbContext context)
    {
        _context = context;    
    }


    // GET: CompanyDetailsViewModels/Edit/5
    public IActionResult Edit()
    {

        var Company = _context.CompanyDetails.First();
        if (Company == null)
        {
            return HttpNotFound();
        }
        var CompanyDetails = new CompanyDetailsViewModel();

        CompanyDetails.CompanyDetailsId = Company.CompanyDetailId;
        CompanyDetails.CompanyName = Company.CompanyName;
        CompanyDetails.ABN = Company.ABN;

        return View(CompanyDetails);
    }

    // POST: CompanyDetailsViewModels/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Edit(CompanyDetailsViewModel companyDetailsViewModel)
    {
        if (ModelState.IsValid)
        {
            CompanyDetail Company = _context.CompanyDetails.First();
            var CompanyDetails = new CompanyDetailsViewModel();

            Company.CompanyName = CompanyDetails.CompanyName;
            CompanyDetails.ABN = Company.ABN;

            _context.CompanyDetails.Update(Company);
            _context.SaveChanges();

            return RedirectToAction("Index");
        }
        return View(companyDetailsViewModel);
    }

}

A view with a (vanilla scaffolded) form - it uses the viewmodel as the model:

<form asp-action="Edit">
<div class="form-horizontal">
    <h4>CompanyDetailsViewModel</h4>
    <hr />
    <div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
    <input type="hidden" asp-for="CompanyDetailsId" />
    <div class="form-group">
        <label asp-for="ABN" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="ABN" class="form-control" />
            <span asp-validation-for="ABN" class="text-danger" />
        </div>
    </div>
    <div class="form-group">
        <label asp-for="CompanyName" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="CompanyName" class="form-control" />
            <span asp-validation-for="CompanyName" class="text-danger" />
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Save" class="btn btn-default" />
        </div>
    </div>
</div>

and the actual custom validation class:

public class ExcludeChar : ValidationAttribute
{
    private readonly string _chars;
    public ExcludeChar(string chars)
    : base("{0} contains invalid character.")
    {
        _chars = chars;
    }


    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value != null)
        {
            for (int i = 0; i < _chars.Length; i++)
            {
                var valueAsString = value.ToString();
                if (valueAsString.Contains(_chars[i]))
                {
                    var errorMessage = FormatErrorMessage(validationContext.DisplayName);
                    return new ValidationResult(errorMessage);
                }
            }
        }
        return ValidationResult.Success;
    }
}
3
  • If I change "<div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>" in the view cshtml to "ValidationSummary.All" the error message shows but only at the top of the page - not under textbox. If I clear the textbox then the "requred" error shows in two places... For some reason custom validation only shows for the summary and only if its set to "all" Commented Apr 13, 2016 at 9:57
  • Does this functionality even exist for MVC6? Commented Apr 13, 2016 at 12:52
  • Is there a specific reason why you're not using built in html helpers? Commented Apr 13, 2016 at 16:01

2 Answers 2

1

I figured it out in your code you are using

<span asp-validation-for="number" class="text-danger" />

Thats default html generated by Visual Studio (no idea why). You need to add the closing tag. Use it like

<span asp-validation-for="number" class="text-danger" ></span>

and it'll show the error messages right under the field.

Sign up to request clarification or add additional context in comments.

Comments

0

What you have to do is specify in the view where you want your custom error message to be shown.

example:

<div>
    @Html.ValidationMessage("CreditRating")
</div>

Then returning a ValidationResult that relates to the "member" CreditRating will show in that part of the view. member is in quotes because the name can actually be any name, does not have to be a real name of a real property.

   results.Add(new ValidationResult("NO NO NO", new[] { "CreditRating" }));

I agree that this is surprising behavior. I seems like custom errors are handled differently. maybe some naming convention that is not documented.

1 Comment

what suppose it should be "results" ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.