S
S
stcmd042362016-09-13 17:00:32
ASP.NET
stcmd04236, 2016-09-13 17:00:32

How to properly create a DataAnnotations validator?

Good day to all! The problem is this. Created a validator based on the ValidationAttribute from the System.DataAnnotations namespace to validate class fields. There is a class with required fields that are marked with the Requred attribute. And also there are unmarked ones. The custom validator applies to both required and optional fields. If values ​​for optional fields are not specified, then after validation it gives an error that these fields (not mandatory) are also required. In theory, non-mandatory fields should not be checked if it does not matter.
Here is the custom class:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    sealed public class APIDataTypes : ValidationAttribute
    {
        public ApiDataType DataType { get; set; }

        public APIDataTypes( ApiDataType DataType) : base()
        {
            this.DataType = DataType;
            this.checkers = new Dictionary<ApiDataType, Func<string, bool>>()
            {
                {ApiDataType.UUID, (value) => 
                {
                    ErrorMessage = "Некорректный идентификатор.";
                    string pattern = @"^[0-9A-Fa-f]{8}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{4}\-[0-9A-Fa-f]{12}$";
                    return Regex.IsMatch(value, pattern);
                }},
                {ApiDataType.Coordinate, (value)=>
                {
                    ErrorMessage = "Некорректные данные координат.";
                    string pattern = @"\d{1,3}\.\d+[;]\d{1,3}\.\d+";
                    return Regex.IsMatch(value, pattern);
                }},
                {ApiDataType.IMEI,(value) =>
                {
                    ErrorMessage = "Некорректный IMEI";
                    string pattern = @"\d{2}\-\d{6}\-\d{6}";
                    return Regex.IsMatch(value, pattern);
                }},
                {
                    ApiDataType.PhoneNumber, (value) => 
                {
                    ErrorMessage = "Некорректный номер. Номер должен состоять только из натуральных чисел. Количество символов должен быть от 12 до 15 шт.";
                    string pattern = @"\d{12,15}";
                    return Regex.IsMatch(value,pattern);
                }},
                {ApiDataType.Version, (value) =>
                {
                    ErrorMessage = "Проверьте корректность данные о версии объекта";
                    Version version = new Version();
                    return Version.TryParse(value, out version);
                }}
            };
        }

        private Dictionary<ApiDataType, Func<string, bool>> checkers;

        public override bool IsValid(object value)
        {   
            return value == null ? false : checkers[this.DataType](value.ToString());
        }
        
    }

    public enum ApiDataType
    {
        PhoneNumber,
        UUID,
        IMEI,
        Coordinate,
        Version
    }

And this is the class that this custom validator is applied to:
public class BeaconBindingModelUpdate
    {

        [Required]
        [APIDataTypes ( ApiDataType.UUID )]
        [Display ( Name = "Идентификатор устройства" )]
        public Guid Id { get; set; }


        [Required]
        [APIDataTypes ( ApiDataType.UUID )]
        [Display ( Name = "Идентификатор пользователья" )]
        /// <summary>
        /// Идентификатор пользователья
        /// </summary>
        public Guid UserId { get; set; }

        [Display ( Name = "Идентификатор типа устройства в системе" )]
        [APIDataTypes ( ApiDataType.UUID )]
        /// <summary>
        /// Идентификатор типа устройства в системе
        /// </summary>
        public Guid BeaconTypeId { get; set; }

        [Display ( Name = "IMEI идентификатор устройства" )]
        [APIDataTypes ( ApiDataType.IMEI )]
        /// <summary>
        /// IMEI идентификатор устройства
        /// </summary>
        public string IMEI { get; set; }

        [Display ( Name = "Версия программного обеспечения" )]
        [APIDataTypes ( ApiDataType.Version )]
        /// <summary>
        /// Версия программного обеспечения устройства
        /// </summary>
        public Version SoftwareVersion { get; set; }

        [Display ( Name = "Версия устройства" )]
        [APIDataTypes ( ApiDataType.Version )]
        /// <summary>
        /// Версия устройства
        /// </summary>
        public Version HardwareVersion { get; set; }

        [Display ( Name = "Пароль установленный на устройстве" )]
        [DataType ( DataType.Password )]
        /// <summary>
        /// Пароль установленный в устройстве
        /// </summary>
        public string DevicePassword { get; set; }

        [Display ( Name = "Номер сим карты" )]
        [APIDataTypes ( ApiDataType.PhoneNumber )]
        /// <summary>
        /// Номер сим карты который установлен в устройстве
        /// </summary>
        public string SimNumber { get; set; }

        [APIDataTypes ( ApiDataType.IMEI )]
        /// <summary>
        /// IMEI идентификатор сим карты
        /// </summary>
        public string SimIMEI { get; set; }

    }

Answer the question

In order to leave comments, you need to log in

1 answer(s)
M
MrDywar Pichugin, 2016-09-13
@stcmd04236

1) metanit.com/sharp/mvc5

Глава 7. Метаданные и валидация модели
Аннотации данных для отображения свойств
Основы валидации
Атрибуты валидации
Валидация модели в контроллере
Отображение ошибок валидации
Создание собственной логики валидации

2) FluentValidation, model separately, validation separately.
https://chsakell.com/2015/08/23/building-single-pa...
public class MovieViewModel : IValidatableObject
{
    public int ID { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public string Image { get; set; }
    public string  Genre { get; set; }
    public int GenreId { get; set; }
    public string Director { get; set; }
    public string Writer { get; set; }
    public string Producer { get; set; }
    public DateTime ReleaseDate { get; set; }
    public byte Rating { get; set; }
    public string TrailerURI { get; set; }
    public bool IsAvailable { get; set; }
    public int NumberOfStocks { get; set; }
 
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var validator = new MovieViewModelValidator();
        var result = validator.Validate(this);
        return result.Errors.Select(item => new ValidationResult(item.ErrorMessage, new[] { item.PropertyName }));
    }
}

public class MovieViewModelValidator : AbstractValidator<MovieViewModel>
    {
        public MovieViewModelValidator()
        {
            RuleFor(movie => movie.GenreId).GreaterThan(0)
                .WithMessage("Select a Genre");
 
            RuleFor(movie => movie.Director).NotEmpty().Length(1,100)
                .WithMessage("Select a Director");
 
            RuleFor(movie => movie.Writer).NotEmpty().Length(1,50)
                .WithMessage("Select a writer");
 
            RuleFor(movie => movie.Producer).NotEmpty().Length(1, 50)
                .WithMessage("Select a producer");
 
            RuleFor(movie => movie.Description).NotEmpty()
                .WithMessage("Select a description");
 
            RuleFor(movie => movie.Rating).InclusiveBetween((byte)0, (byte)5)
                .WithMessage("Rating must be less than or equal to 5");
 
            RuleFor(movie => movie.TrailerURI).NotEmpty().Must(ValidTrailerURI)
                .WithMessage("Only Youtube Trailers are supported");
        }
 
        private bool ValidTrailerURI(string trailerURI)
        {
            return (!string.IsNullOrEmpty(trailerURI) && trailerURI.ToLower().StartsWith("https://www.youtube.com/watch?"));
        }
    }

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question