Vladislav Kosovskikh
Vladislav Kosovskikh

Reputation: 476

Change the default ErrorMessage of model validation attribute asp.net core 3.1

How to avoid writing [Required(ErrorMessage="My custom error message")] every time and set the default ErrorMessage for whole project?

Upvotes: 5

Views: 15558

Answers (3)

Vladislav Kosovskikh
Vladislav Kosovskikh

Reputation: 476

After some research I have found the only way to use some sort of localization and resource files. You still need to write [Required(ErrorMessage="<Resource Key Goes Here>")]. But the advantage of just using constant messages like this [Required(ErrorMessage=Class.Property)] is that you can access attribute variables in resource file.

This is how I did it:

  1. Create a folder in root named Resources
  2. Create an empty class in Resources folder named ValidationMessages.cs
  3. Create an empty file in Resources folder named ValidationMessages.resx
  4. Edit the .resx file with VisualStudio editor
  5. Example key,value pair for [StringLength] attribute: "StringLength","Min length {2}, max length {1}"
  6. In model use attribute [StringLength(20, MinimumLength = 10, ErrorMessage = "StringLength")]
  7. Add this code to Startup.cs
    using MyProject.Resources;
    ...
    public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc()
                .AddDataAnnotationsLocalization(options => {
                    options.DataAnnotationLocalizerProvider = (type, factory) =>
                        factory.Create(typeof(ValidationMessages));
                });
        ...

It's done.

Upvotes: 3

LouraQ
LouraQ

Reputation: 6891

How to avoid writing [Required(ErrorMessage="My custom error message")] every time and set the default ErrorMessage for whole project?

You can create custom attributes to automatically generate error messages, but you inevitably need to add attributes to the corresponding fields to represent your constraints on the fields.


Update

If you have multiple different validation requirements, then you can merge them into a custom attribute, and inherit the ValidationAttribute class.

The premise is that you need to pass parameters to distinguish them.

Here is a case:

public class CustomRequired : ValidationAttribute
    {
        public CustomRequired(string sepcialType)
        {
            this.SepcialType = sepcialType;
        }
        public string SepcialType { get; private set; }  

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            string errorType;
            if (value == null)
            {
                errorType = "required"; 
            }
            else if (!string.IsNullOrEmpty(SepcialType) && !IsValidEmail(value.ToString()))
            {
                errorType = "not valid";
            }
            else
            {
                return ValidationResult.Success;
            }
            ErrorMessage = $"{validationContext.DisplayName}  field is {errorType}.";
            return new ValidationResult(ErrorMessage);
        }
        bool IsValidEmail(string email)
        {
            try
            {
                var addr = new System.Net.Mail.MailAddress(email);
                return addr.Address == email;
            }
            catch
            {
                return false;
            }
        }
    }
  

Class:

 public class ThemTest
    {
        public int Id { get; set; }
        [CustomRequired("")]
        public string Name { get; set; }
        [CustomRequired("")]
        public string Theme { get; set; }
        [CustomRequired("Email")]
        public string Email { get; set; }
    }

Validate view:

@model ThemTest
@{
    ViewData["Title"] = "TestAttr";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h1>TestAttr</h1>
<form method="post">
    <div class="form-row">
        <label asp-for="Name"></label>
        <input asp-for="Name" type="text" class="form-control" />
        <span asp-validation-for="Name" class="field-validation-valid text-danger"></span>
    </div>
    <div class="form-row">
        <label asp-for="Theme"></label>
        <input asp-for="Theme" type="text" class="form-control" />
        <span asp-validation-for="Theme" class="field-validation-valid text-danger"></span>
    </div>
    <div class="form-row">
        <label asp-for="Email"></label>
        <input asp-for="Email" type="text" class="form-control" />
        <span asp-validation-for="Email" class="field-validation-valid text-danger"></span>
    </div>
    <input id="Submit1" type="submit" value="submit" class="btn btn-primary" />
</form>

Here is the test result:

enter image description here

Upvotes: 0

jawsofdoom
jawsofdoom

Reputation: 297

Here's how I do it.

In Startup.cs -> Configure(), set up your exception handler middleware:

app.UseExceptionHandler("/Home/Error");

This redirects all exceptions to the home controller error method. Now set up your viewmodel for Home/Error.

In ErrorViewModel.cs:

public class ErrorViewModel
{
    public string RequestId { get; set; }   
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);    
    public string ExceptionMessage { get; set; }
    public bool ShowExceptionMessage => !string.IsNullOrEmpty(ExceptionMessage);
}

In HomeController:

[AllowAnonymous]
public IActionResult Error()
{
    ErrorViewModel errorViewModel = new ErrorViewModel();
    errorViewModel.RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

    string message = "your message";
    errorViewModel.ExceptionMessage = message;
    _logger.LogError(message);
    return View(errorViewModel);
}

At this point your custom error message will be accessible in the model of your Error view page.

Upvotes: -1

Related Questions