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

مقدمه

این مقاله به بررسی اعتبارسنجی فایل بر اساس محتوا می پردازد. در مقاله قبلی، نحوه تشخیص ماهیت یک فایل یا Mimetype آن را بررسی کردیم. یکی از کاربردهای تشخیص Mimetype ، اعتبارسنجی فایل بر اساس محتوا است برای مثال وقتی که فقط فایل های تصویری قابل پذیرش هستند.

در ادامه برای اعتبارسنجی فایل یک کلاس به اصطلاح Validator می نویسیم و از آن به صورت یک Attribute در بالای هر Property استفاده می کنیم.

کدهای زیر در زمان نگارش این مقاله بر مبنای 6 Net Core. نوشته شده است.

نمونه ای  از خطای  عدم پذیرش نوع فایل
نمونه ای از خطای عدم پذیرش نوع فایل


برای شروع ابتدا باید یک Attribute برای انجام اعتبارسنجی تعریف کنیم. کلاسی تعریف می کنیم که از System.Attribute و همچنین از اینترفیس IModelValidator ارث بری کند. ترکیب این دو باعث می شود که ما بتوانیم Attributeی تعریف کنیم که در هنگام اعتبارسنجی به آن مراجعه شود و برای Bind کردن اطلاعات به یک Property ، درستی اطلاعات بررسی شود.

کلاس را به صورت زیر تعریف می کنیم :

public class FileValidate :Attribute, IModelValidator
{
}
دقت کنید که اینترفیس IModelValidator در فضای نام زیر قرار دارد:
Microsoft.AspNetCore.Mvc.ModelBinding.Validation.IModelValidator

در ادامه این کلاس به عنوان یک Attribute استفاده می شود. حال یک ViewModel برای دریافت فرم اطلاعات از کاربر به صورت زیر تعریف می کنیم :

public class FileValidationViewModel
{
        public string            Title         { get; set; }
	public IFormFile      Logo       { get; set; }
}

برای استفاده از Attributeی که در بالا تعریف کرده ایم و با توجه به اینکه می خواهیم برای اعتبارسنجی فایل ها از آن استفاده کنیم کد کلاس FileValidationViewModel به صورت زیر تغییر خواهد کرد.

public class FileValidationViewModel
{
	public string Title { get; set; }

	[FileValidate]
        [DataType(DataType.Upload)]
        public IFormFile { get; set; }
{	

برای توسعه بیشتر کلاس FileValidate ابتدا باید متد Validate متعلق به انترفیس IModelValidator پیاده سازی کنیم. در این متد محتوای فایل را از لحاظ داشتن مقدار ، حداکثر اندازه فایل و همچنین فرمت یا Mimetype فایل را بررسی می کنیم و در صورت بروز هر گونه خطایی در اعتبارسنجی آن را گزارش می دهیم (ModelState).

ابتدا چند Property به کلاس اضافه می کنیم تا مشخص شود که چطور خطاها را بررسی کنیم.

public class FileValidate :Attribute, IModelValidator
{
        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; }
}

برای هر حالتی از اعتبارسنجی پیغام مناسب آن را در ModelState قرار می دهیم. حال نوبت خود متد Validate می رسد که به شکل زیر پیاده سازی می شود.

public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
        {
            var file = context.Model as IFormFile;
            //IsRequired Validation
            if (file == null || file.Length ==0 )
            {
                if (IsRequied)
                {
                    return new List<ModelValidationResult>()
                    {
                        new ModelValidationResult
                            (
                                memberName  : context.ModelMetadata.PropertyName,
                                message     : RequiredErrorMessage
                            ),
                    };
                }
                else
                {
                    return Enumerable.Empty<ModelValidationResult>();
                }
            }
            // File size validation
            if (file.Length > MaximumFileSize)
            {
                return new List<ModelValidationResult>()
                {
                    new ModelValidationResult
                    (
                        memberName  : context.ModelMetadata.PropertyName,
                        message     : MaximumFileSizeErrorMessage
                    ),
                };
            }
            // Mimetype validation
           using (var stream = file.OpenReadStream())
            {
                var mimetype = stream.GetMimeType();
               if (mimetype == null)
                {
                    return new List<ModelValidationResult>()
                    {
                        new ModelValidationResult
                        (
                            memberName  : context.ModelMetadata.PropertyName,
                            message     : ExtensionAcceptableErrorMessage
                        ),
                    };
                }
                if (!Extensions.Contains(mimetype.Extension))
                {
                    return new List<ModelValidationResult>()
                    {
                        new ModelValidationResult
                        (
                            memberName  : context.ModelMetadata.PropertyName,
                            message     : ExtensionAcceptableErrorMessage
                        ),
                    };
                }
            }
            return new List<ModelValidationResult>();
        }

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

نکته قابل اشاره در کد بالا می تواند این باشد که ما به محض برخورد با یک خطا لیستی از نوع ModelValidationResult را بر می گردانیم که در آن مشخص می کنیم که متن خطا چیست و خطا متعلق به چه Property می باشد.
return new List<ModelValidationResult>()
{
new ModelValidationResult
(
memberName : context.ModelMetadata.PropertyName,
message : ExtensionAcceptableErrorMessage
),
};

در صورتی که خطایی در اعتبارسنجی ثبت شود مقدار ModelState.IsValid برابر False خواهد شد. در انتها نحوه استفاده از Attribute به شکل زیر تغییر خواهد کرد.

public class FileValidationViewModel
    {
        [FileValidate(
            IsRequied = false, 
            RequiredErrorMessage = &quotPlease, select your logo&quot,
            MaximumFileSize = 2 * 1024 * 1024, //in bytes
            MaximumFileSizeErrorMessage = &quotYour file is too big&quot,
            ExtensionAcceptable = &quotjpg,bmp,png&quot, 
            ExtensionAcceptableErrorMessage = &quotYour logo not supported&quot)]
        [DataType(DataType.Upload)]
        public IFormFile     Logo        { get; set; }
    }

روش دیگر

روش دیگری نیز برای نوشتن یک Attribute جهت بررسی اعتبارسنجی وجود دارد و آن هم استفاده از کلاس ValidationAttribute می باشد. نحوه استفاده بدین صورت است که یک کلاس را تعریف می کنیم که از آن ارث بری کند و سپس متد IsValid آن را Override می کنیم بدین صورت کدهای ما کمی خواناتر خواهد شد. بقیه موارد مثل کدهایی است که در بالا اشاره کردیم.

public class OtherFileValidate : ValidationAttribute
{
        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; }
	protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
         }
}

فقط دقت داشته باشید که متد IsValid مقداری از نوع ValidationResult را برمیگرداند. در صورتی که ما با خطای اعتبارسنجی مواجه شدیم کافی است که متن خطا را در قالب شیء از این نوع به شکل زیر برگردانیم :

return new ValidationResult(ExtensionAcceptableErrorMessage);

و در صورتی که هیچ خطایی موجود نباشد می توانیم از کد زیر هم استفاده کنیم:

return ValidationResult.Success;

آدرس GitHub

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

سخن آخر

در این مقاله دو روش را برای اعتبارسنجی فایل به کمک نوشتن یک Attribute سفارشی و با استفاده از کتابخانه MimeTypeDetective را بررسی کردیم. هر کدام از روش ها می تواند به صورت عملی در یک پروژه بکار گرفته شود. در ادامه این سری از مقالات روش دیگری را با استفاده از کتابخانه محبوب FluentValidation برای اعتبارسنجی یک فایل شرح خواهیم داد.

در زیر لینک سایر مقالات این سری را مشاهده می کنید:

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


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