اعتبارسنجی فایل بر اساس محتوا در FluentValidation

مقدمه

این مقاله پایان سری مقالات اعتبارسنجی فایل بر اساس محتوا در net. است و در این قسمت به بررسی اعتبارسنجی فایل به کمک کتابخانه FluentValidation خواهیم پرداخت.


کتابخانه FluentValidation برای ساخت قوانین اعتبارسنجی کاربرد دارد. شاید قبلا با برخی از Attribute های فضای نام System.ComponentModel.DataAnnotations کار کرده باشید برای نمونه Required. این کلاس ها به DataAnnotation معروفند و به صورت Attribute تعریف می شوند که در بالای Property قرار می گیرند و نحوه اعتبارسنجی داده را مشخص می کنند.

ولی در کتابخانه FluentValidation در واقع این قوانین اعتبارسنجی به صورت توابع Fluent اعمال می شوند یک نمونه از کاربرد آن را در زیر می توانید ببینید و برای توضیحات بیشتر می توانید به وب سایت خود کتابخانه https://fluentvalidation.net مراجعه کنید.

public class CustomerValidator : AbstractValidator<Customer>
{
  public CustomerValidator()
  {
    RuleFor(x => x.Surname).NotEmpty();
    RuleFor(x => x.Forename).NotEmpty().WithMessage(&quotPlease specify a first name&quot);
    RuleFor(x => x.Discount).NotEqual(0).When(x => x.HasDiscount);
    RuleFor(x => x.Address).Length(20, 250);
    RuleFor(x => x.Postcode).Must(BeAValidPostcode).WithMessage(&quotPlease specify a valid 
                  postcode&quot);
  }
  private bool BeAValidPostcode(string postcode)
  {
    // custom postcode validating logic goes here
   }
}
دقت کنید در عمل تفاوت FluentValidation و DataAnnotations در این است که FluentValidation قواعد اعتبارسنجی را برای یک کلاس تعریف می کند ولی برای DataAnnotation اغلب ما این قواعد را برای یک Property تعریف می کنیم.

این کتابخانه دارای کلاسی به اسم PropertyValidator می باشد که با کمک آن می توانید یک قاعده اعتبارسنجی را تعریف کنید و سپس در هر جایی که لازم بود بارها از آن استفاده کنید.

حال می خواهیم قاعده ای را برای اعتبارسنجی فایل ها همانند مقاله قبل تعریف کنیم که از نظر انداره و محتوا اعتبارسنجی را به کمک کتابخانه MimetypeDetective انجام دهیم.

ابتدا باید کلاسی را که از PropertyValidator ارث بری می کند تعریف کنیم:

public class FileValidation<T, TProperty> : PropertyValidator<T, TProperty>
{
}

همانطور که می بینید این کلاس به صورت جنریک می باشد و اساس کار این کتابخانه دقیقا به این صورت که به کلاس وابسته است و در واقع اعتبارسنجی را برای یک strongly-typed انجام می دهد. بعد از تعریف کلاس باید متد IsValid را Override کنیم یعنی مجددا پیاده سازی کنیم. قبل از این کار تعدادی Property تعریف می کنیم تا قواعد را مشخص کنیم و پیغام مناسب برای هر کدام را برگردانیم.

در نهایت کد بالا به صورت زیر تغییر می کند :

public class FileValidation<T, TProperty> : PropertyValidator<T, TProperty>
    {
        public bool          IsRequied                                                { get; set; } = false;
        public string       RequiredErrorMessage                          { get; set; }
        public int             MaximumFileSize                                   { get; set; }
        public string       MaximumFileSizeErrorMessage           { get; set; }
        public string       ExtensionAcceptable                              { get; set; }
        public string       ExtensionAcceptableErrorMessage     { get; set; }

        private IEnumerable<string> Extensions
        {
            get
            {
                return ExtensionAcceptable.Split(',')
                    .Select(x =>
                        x.Trim().StartsWith('.')
                            ? x.Trim().ToLower()
                            : &quot.&quot + x.Trim().ToLower()
                    );
            }
        }
        public override bool IsValid(ValidationContext<T> context, TProperty value)
        {
            var file = value as IFormFile;
            if (file == null || file.Length == 0)
            {
                context.AddFailure(context.PropertyName,RequiredErrorMessage);
                return false;
            }
            if (file.Length > MaximumFileSize)
            {
                context.AddFailure(context.PropertyName, MaximumFileSizeErrorMessage);
                return false;
            }
            using (var stream = file.OpenReadStream())
            {
                var mimetype = stream.GetMimeType();
                if (mimetype == null)
                {
                    context.AddFailure(context.PropertyName, ExtensionAcceptableErrorMessage);
                    return false;
                }
                if (!Extensions.Contains(mimetype.Extension))
                {
                    context.AddFailure(context.PropertyName, ExtensionAcceptableErrorMessage);
                    return false;
                }
            }
            return true;
        }
        public override string Name { get; }
        protected override string GetDefaultMessageTemplate(string errorCode)  => &quot&quot
    }

در کد بالا ما تعدادی پراپرتی تعریف کرده ایم که فایل را بر اساس داشتن محتوا ، اندازه فایل و همچنین نوع فایل قابل پذیرش بررسی می کند و به ازای هر کدام هم پیغام مناسب را به کاربر در صورت بروز خطا برمیگرداند.

مقدار برگشتی تابع IsValid از نوع bool می باشد بدین معنا که اگر قاعده ای نقض شد مقدار False را بر می گردانیم و متن خطا را در Context به صورت زیر اضافه می کنیم.

context.AddFailure(context.PropertyName, ExtensionAcceptableErrorMessage);
دقت کنید که در این کلاس ما پسوندهایی که برای اعتبارسنجی قابل پذیرش هستند را به صورت پسوند و با کاما از هم جدا کرده ایم.

حالا می خواهیم برای کلاس زیر که فرم اطلاعات را از کاربر دریافت می کند یک کلاس اعتبارسنجی بنویسیم.

public class FileValidationViewModel
    {
        [DataType(DataType.Upload)]
        public IFormFile Logo { get; set; }
    }

برای این کار باید یک کلاس اعتبارسنجی تعریف کنیم که از AbstractValidator ارث بری میکند :

public class FileValidationViewModelValidator : AbstractValidator<FileValidationViewModel>    {
        public FileValidationViewModelValidator()
        {
            var fileValidator = new FileValidation<FileValidationViewModel, IFormFile>()
            {
                IsRequied                                               = false,
                RequiredErrorMessage                        = &quotPlease, select your logo&quot,
                MaximumFileSize                                 = 2 * 1024 * 1024,
                MaximumFileSizeErrorMessage        = &quotYour file is too big&quot,
                ExtensionAcceptable                           = &quotjpg,bmp,png&quot,
                ExtensionAcceptableErrorMessage  = &quotYour logo not supported&quot
            };
            RuleFor(x => x.Logo).SetValidator(fileValidator);
        }
    }

همانطور که مشاهده می کنید کلاس FileValidationViewModelValidator در اصطلاح یک کلاس Validator می باشد و کار آن تعریف قواعد اعتبارسنجی برای کلاس FileValidationViewModel می باشد و از AbstractValidator ارث بری کرده است که به صورت جنریک به کلاس FileValidationViewModel اشاره دارد.

در ادامه کد همانطور که مشاهده می کنید از کلاس FileValidation نمونه سازی کرده ایم و به کمک تابع SetValidator آن را به Property مربوطه آن یعنی Logo اختصاص داده ایم. بدین صورت محتوای فایل اعتبارسنجی می شود.

آدرس GitHub

کد کامل مقاله فعلی را می توانید از آدرس گیت هاب مشاهده کنید و نحوه استفاده از آن را در یک پروژه کامل مشاهده کنید.

سخن آخر

در طول این مقالات سعی شد که نحوه ی اعتبارسنجی یک فایل بر اساس محتوا را بررسی کنیم؛ با mimetype آشنا شدیم و در کدهایی که برای اعتبارسنجی نوشتیم از آن استفاده کردیم؛ از کتابخانه های MimetypeDetective و کتابخانه محبوب FluentValidation در کدهایی که نوشتیم استفاده کردیم.

امیدوارم که این مقالات برای شما مفید و قابل استفاده بوده باشند. در ادامه آدرس سایر مقالات را مشاهده می کنید.

نحوه تشخیص Mimetype یک فایل بر اساس محتوای آن در Net.
اعتبارسنجی فایل بر اساس محتوا در Net.


توجه داشته باشید که این مقاله ممکن است به مرور زمان تغییر کند یا اصطلاحاتی در آن صورت گیرد.