Attribute در C#، به تگهای Metadata گفته میشود که به عناصر مختلف برنامه مانند کلاسها، متدها، Property و … اضافه شده و باعث افزودن ویژگیهای خاصی به آن عناصر میشود. در C#، ویژگیهای (Attribute) از پیش تعریف شده زیادی وجود دارند که هر کدام میتوانند کارکردهای متفاوتی داشته باشند، به عنوان مثال میتوانند به عنوان یک تگ اعتبارسنجی روی یک Property قرار بگیرند یا رفتار یک اکشن Api را مشخص کنند.
همچنین می توانیم بر حسب نیاز Attributeهای اختصاصی را در زمان توسعه نرمافزار برای خودمان تعریف کنیم. به عنوان مثال شاید برای شما هم این مساله بوجود آمده باشد که یک Property کد ملی در سامانه خود تعریف کرده باشید، این کد ملی نیازمند اعتبارسنجی (از لحاظ تعداد ارقام، عدم وجود حروف، پیروی از قاعده کد ملی، …) است.
روش نامطلوب این است که در هر بار تعریف کد ملی ،این اعتبارسنجی ها انجام شود، ولی با تعریف یک Attribute، کافیست آن را روی Propertyهای کدملی در همه مدلهایی که تعریف کردهاید بگذارید تا هم کد زیباتری داشته باشید و هم خوانایی بیشتری به دست بیاورید.
در این مقاله قصد داریم ابتدا با نحوه استفاده از Attributeهای کاربردی در C# آشنا شویم و سپس به پیادهسازی یک Attribute اختصاصی میپردازیم و در نهایت با Filterها که یکی از مهمترین کاربردهای Attributeها هستند آشنا میشویم. با ما همراه باشید.
نحوه استفاده از انواع Attribute
برای استفاده از Attribute باید نام آن را درون براکت و بالاتر از عنصر مربوطه استفاده کرد. در ادامه، به بعضی از Attributeها اشاره میکنیم:
HttpMethod
تگهای HttpMethod در WebApi و WebApp استفاده میشود و نشاندهنده این است یک اکشن چه عملیاتی را انجام می دهد. به عنوان مثال برای مشخص کردن عملیات CRUD از HttpGet، HttpPost، HttpPut ، HttpDelete استفاده می کنیم.
Obsolete
Attribute Obsolete نشان دهنده این است که آن بخش از کد منسوخ یا به عبارتی deprecated شده است و دیگر نباید از آن استفاده کرد ، بنابراین احتمال جایگزین جدیدی برای آن توسعه داده شده است. به عنوان مثال از داتنت ۶ به بعد سرویسهای WebRequest, WebClient و ServicePoint منسوخ شده است و تگ Obsolete بر این موضوع تاکید دارد و استفاده از این سرویسها برای توسعه دهندگان توصیه نمیشوند. با این حال که این کلاسها همچنان در دسترس هستند ولی بهتر است به جای آنها از HttpClient یا IHttpClientFactory با مزیتهای بیشتر استفاده نمود.
DataAnnotations
یکی از مهمترین کاربردهای Attributeهایی که در فضای نام System.ComponentModel.DataAnnotations هستند، کنترل اعتبارسنجی Propertyها در DataModel است. مثلا StringLengthAttribute مشخص کننده طول فیلدهای رشتهای است ، یا RequiredAttribute مشخص کننده NotNull بودن فیلدهاست.
سایر Attributeها
در داتنت، Attributeهای پرکاربردی دیگری هم مثل SerializableAttribute و ControllerAttribute، ApiControllerAttribute وجود دارند که بررسی بیشتر آن ها را به عهده خودتان می گذاریم.
Filters Attribute
Filter Attributeها، فیلترهای اجرایی هستند که میتوانند قبل یا بعد از یک اکشن یا کنترلر در WebApiو WebApp قرار بگیرند. فیلترهای از پیش تعریف شده در داتنت وجود دارند و ایجاد فیلتر اختصاصی نیز امکان پذیر است. بطور کلی Filter Attribute ها به ۴ نوع کلی دسته بندی می شوند که عبارتند از:
- Authorize: این تگ برای جداسازی کاربرانی که تصدیق هویت شدهاند استفاده میشود.
- Action Filters
- RequireHttps: این تگ درخواستهای HTTP ناامن را مجبور میکند که از طریق HTTPS ارسال شوند.
- Results Filters:
- OutputCache: این تگ خروجی Action را برای مدت زمان مشخصی Cache میکند.
- Exception Filters
- HandleError: این تگ برای مدیریت خطا استفاده میشود.
ایجاد Attribute اختصاصی
در داتنت با ارث بری از کلاسها و اینترفیسهای زیر، امکان ایجاد Attribute دلخواه وجود دارد که به اختصار به چند نوع از آنها با مثال اشاره میکنیم:
System.Attribute
برای ایجاد این نوع Attribute باید یک کلاس بسازید که از کلاس System.Attribute ارث بری کند. برای استفاده از آن میتوانید روی کلاسها یا هر چیز دیگری اعمال کنید. در مثال زیر یک کلاس FirstAttribute تعریف شده است.
public class FirstAttribute : Attribute { public FirstAttribute(string name) { Name=name } public string Name { get;private set; } }
و به صورت زیر از آن استفاده میکنیم:
[First("Key")] public string Key { get; set; }
ConsumesAttribute
ConsumesAttribute مشخص میکند که Actionها content type مشخص یا تنها نوع خاصی از داده را بپذیرند. در مثال زیر مشخص شده است ورودی فایل استریم فقط از نوع عکس قابل قبول است:
public class OctetStreamConsumesAttribute : ConsumesAttribute { public OctetStreamConsumesAttribute() : base("application/octet-stream", new string[] { "image/jpg", "image/jpeg", "image/png", "image/tiff", "image/tif" } ) { } }
و به شکل زیر برای اکشنهایی که ورودی استریم دارند، از آن استفاده میکنیم:
[OctetStreamConsumes] public async Task<IActionResult> UploadFile([FromBody] Stream file) { // file processing here }
از ConsumesAttribute میتوانیم مستقیم روی اکشنها به صورت زیر هم استفاده کنیم:
[Consumes("application/json")] public async Task<IActionResult> Get(object obj)
IActionFilter
Action Filterها برای پیادهسازی منطقهایی که قبل یا بعد از اجرای یک action اجرا میشوند، مورد استفاده قرار میگیرد. این اینترفیس دو قرارداد OnActionExecuting و OnActionExecuted دارد که با ارث بری از آن باید پیادهسازی شود.
در مثال زیر، مقدار توکن در هدر درخواست چک شده است:
public class HasTokenAttribute : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { var token = context.HttpContext.Request.Headers.FirstOrDefault(x => x.Key == "Authorization"); if (token.Key == null) { throw new UnauthorizedAccessException(); } public void OnActionExecuted(ActionExecutedContext context) } }
نکتهای که باید به آن توجه کنید این است که پس از توسعه این نوع فیلترها ، باید آنها را در dependency injection container داتنت رجیستر کرد:
Services.AddScoped<HasTokenAttribute>()
و برای استفاده به صورت زیر در actionها میتوان از آنها استفاده کرد:
[ServiceFilter(typeof(HasTokenAttribute))] public async Task<IActionResult> CheckHasToken()
نکته: اینترفیس IAsyncActionFilter است همان async شده IActionFilter است. همچنین در نسخههای قدیم داتنت کور، به جای اینترفیس از کلاس ActionFilterAttribute استفاده میشد؛ با این تفاوت که متدهای OnActionExecuting و OnActionExecuted به صورت virtual بوده و میتوانستیم آنها را override کنیم.
IExceptionFilter
این فیلتر برای مدیریت خطاهایی استفاده میشود که به وسیله actionها و یا controllerها ایجاد شدهاند. این اینترفیس متد OnException دارد که با ارث بری از آن باید پیادهسازی شود.
public class NotImplExceptionFilterAttribute : IExceptionFilter { public void OnException(ExceptionContext filterContext) { if (filterContext.Exception is NotImplementedException) { filterContext.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented); } } }
ValidationAttribute
همانطور که در بالا گفته شد، DataAnnotationها اعتبارسنجیهایی هستند که بر روی پراپرتیها اعمال میشوند. برای ایجاد یک اعتبارسنجی اختصاصی مثل آن، باید از ValidationAttribute استفاده کرد. این تگ، یک متد IsValid دارد که باید روی آن override کرد. در مثال زیر اعتبارسنجی کدملی پیادهسازی شده است:
public class NationalCodeAttribute : ValidationAttribute { private bool isNullable = false; public NationalCodeAttribute(bool isNullable) { this.isNullable = isNullable; } public NationalCodeAttribute() : this(false) { } public override bool IsValid(object value) { if (string.IsNullOrEmpty(value?.ToString()?.Trim()) && isNullable) { return true; } string nationalCode = value.ToString(); return IsValidPersonNationalCode(nationalCode); } }
و برای استفاده میتوانید به روش زیر پیادهسازی کنید:
[NationalCode] [Display(Name ="کدملی")] [StringLength(10)] public string PersonNationaCode { get; set; }
IClientModelValidator
اینترفیس IClientModelValidator یک متد به نام AddValidation دارد که این امکان را فراهم میکند تا بتوانیم اعتبارسنجی را در سمت کلاینت انجام دهیم. در مثال بالا که از ValidationAttribute استفاده کردیم، اعتبارسنجی سمت سرور انجام میشود و در صورت استفاده در فرانت در صفحات وب که از jqueryValidation استفاده میکنند، باید از اینترفیس IClientModelValidator ارث بری کرد.
استفاده از Customize Attributeهای پکیجهای nuget
وقتی شما از یک کتابخانه استفاده میکنید ، احتمال دارد Attributeهای اختصاصی داشته باشد که بتوانید آنها را به کار ببرید. به عنوان مثال پکیج Newtonsoft.Json برای تبدیل object به JSON و برعکس (JSON Serialization) استفاده میشود و Attributeهای JsonIgnore و JsonProperty و … در این کتابخانه وجود دارند.
وقتی شما بخواهید یک پراپرتی در زمان دریافت اشیا از ورودی و بازگرداندن آنها به خروجی نمایش داده نشود، از JsonIgnore به صورت زیر استفاده میکنید:
[JsonIgnore] public string HiddenInApi { get; set; }
ترتیب اجرای فیلترها
در شکل زیر ترتیب اجرای فیلترها در پایپلاین داتنت را میبینید:
جمعبندی
در این مقاله سعی کردیم تا قسمتی از Attributeهای C# را بررسی کنیم. هر کدام از این Attributeها برای بخش و عملکرد خاصی کاربرد دارند و شما با کمک این مقاله و نمونههای پیادهسازی Attributeها، میتوانید سایر Attributeها را پیادهسازی کنید.
منابع:
دیدگاهتان را بنویسید