من تجربیات خود را در طی سالهای کاری ام در ویرگول منتشر می کنم. اگر به برنامه نویسی علاقمند هستید این بلاگ را مانند خانه خود بدانید.
اعتبارسنجی فایل بر اساس محتوا در 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 = "Please, select your logo",
MaximumFileSize = 2 * 1024 * 1024, //in bytes
MaximumFileSizeErrorMessage = "Your file is too big",
ExtensionAcceptable = "jpg,bmp,png",
ExtensionAcceptableErrorMessage = "Your logo not supported")]
[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
توجه داشته باشید که این مقاله ممکن است به مرور زمان تغییر کند یا اصطلاحاتی در آن صورت گیرد.
مطلبی دیگر از این انتشارات
استفاده از منوهای آبشاری کافیه
مطلبی دیگر از این انتشارات
نحوه تشخیص Mimetype یک فایل بر اساس محتوای آن در Net.
مطلبی دیگر از این انتشارات
بررسی روش های معماری نرم افزار