مقدمه
اعتبارسنجی اطلاعات (Validation) یکی از ارکان اساسی برای تضمین صحت و امنیت دادههای ورودی به برنامهها محسوب میشود. با ظهور Minimal APIها در چارچوب داتنت، رویکردی سبک و ساده برای ساخت رابطهای برنامهنویسی کاربردی (API) معرفی شد که نیاز به کدهای اضافی و پیچیدگیهای رایج در روشهای سنتی را کاهش داد. با این حال، فقدان پشتیبانی داخلی برای اعتبارسنجی در نسخههای اولیه Minimal APIها، توسعهدهندگان را با چالشهایی مواجه کرد. این مقاله به بررسی سیر تحول اعتبارسنجی در Minimal APIها، از روشهای دستی تا معرفی قابلیتهای پیشرفته در داتنت 10 میپردازد.
معرفی Minimal API و چالشهای اولیه اعتبارسنجی
Minimal APIها که در داتنت 6 معرفی شدند، رویکردی مینیمال برای تعریف نقاط پایانی (Endpoints) در فایل Program.cs ارائه میدهند. این روش با حذف وابستگیهای اضافی و کاهش کدهای تکراری، توسعه سریعتر و سادهتر APIها را ممکن میسازد. با این حال، در نسخههای اولیه، Minimal APIها فاقد پشتیبانی داخلی برای اعتبارسنجی خودکار بودند. این موضوع توسعهدهندگان را مجبور میکرد که اعتبارسنجی را به صورت دستی در بدنه نقاط پایانی پیادهسازی کنند.
به عنوان مثال، فرض کنید میخواهیم یک درخواست برای ایجاد یادداشت (Note) را اعتبارسنجی کنیم. در رویکرد دستی، کد به این صورت خواهد بود:
app.MapPost("/v1/notes", (CreateNoteRequest request) =>
{
if (string.IsNullOrWhiteSpace(request.Value))
{
return Results.BadRequest();
}
if (Guid.Empty == request.Id)
{
return Results.BadRequest();
}
// ذخیره یادداشت در پایگاه داده
return Results.Ok();
});این روش، اگرچه ساده به نظر میرسد، مشکلات متعددی ایجاد میکند. ترکیب منطق اعتبارسنجی با منطق کسبوکار (Business Logic) اصل مسئولیت واحد (Single Responsibility Principle) را نقض میکند. همچنین، آزمایش (Testing) این کدها پیچیدهتر میشود، زیرا باید اعتبارسنجی و منطق کسبوکار به طور جداگانه بررسی شوند.
استفاده از FluentValidation به عنوان راهحل اولیه
برای رفع مشکلات روش دستی، توسعهدهندگان به ابزارهایی مانند FluentValidation روی آوردند. این ابزار امکان تعریف قوانین اعتبارسنجی به صورت جداگانه را فراهم میکند. در این روش، اعتبارسنجی با استفاده از ویژگی [FromServices] و تزریق وابستگی (Dependency Injection) انجام میشود. کد زیر نمونهای از این رویکرد است:
app.MapPost("/v2/notes", async ([FromServices] IValidator<CreateNoteRequest> validator, CreateNoteRequest request) =>
{
var validationResult = await validator.ValidateAsync(request);
if (!validationResult.IsValid)
{
return Results.BadRequest(validationResult.Errors.Select(error => error.ErrorMessage));
}
// ذخیره یادداشت در پایگاه داده
return Results.Ok();
});اگرچه این روش جداسازی بهتری بین اعتبارسنجی و منطق کسبوکار ایجاد میکند، اما همچنان مشکلاتی مانند پیچیدگی در پیکربندی و نیاز به کد اضافی برای هر نقطه پایانی وجود دارد. علاوه بر این، توسعهدهندگانی که به اعتبارسنجی مبتنی بر مدل (Model-Based Validation) در کنترلرهای سنتی عادت داشتند، این روش را غیرطبیعی مییافتند.
معرفی فیلترهای نقطه پایانی در داتنت 7
با انتشار داتنت 7، مفهوم فیلترهای نقطه پایانی (Endpoint Filters) معرفی شد که امکان جداسازی بهتر منطق اعتبارسنجی را فراهم کرد. این فیلترها قبل از اجرای نقطه پایانی، درخواست را بررسی میکنند. کد زیر نمونهای از استفاده از فیلتر اعتبارسنجی است:
app.MapPost("/v3/notes", (CreateNoteRequest request) =>
{
// ذخیره یادداشت در پایگاه داده
return Results.Ok();
})
.AddEndpointFilter<ValidationFilter<CreateNoteRequest>>();
public sealed class ValidationFilter<TRequest>(IValidator<TRequest> validator) : IEndpointFilter
where TRequest : class
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
var argument = context.Arguments.SingleOrDefault(argument => argument?.GetType() == typeof(TRequest));
if (argument is not TRequest request)
{
throw new ArgumentException($"Could not find request with the following type `{typeof(TRequest)}`");
}
var validationResult = await validator.ValidateAsync(request);
if (!validationResult.IsValid)
{
return Results.BadRequest(validationResult.Errors.Select(error => error.ErrorMessage));
}
return await next(context);
}
}این رویکرد، منطق اعتبارسنجی را از بدنه نقطه پایانی جدا میکند و قابلیت استفاده مجدد را افزایش میدهد. با این حال، پیچیدگی پیادهسازی و عدم پشتیبانی مستقیم از اعتبارسنجی مبتنی بر ویژگیها (Data Annotations) همچنان برای برخی توسعهدهندگان مشکلساز بود.
تحول بزرگ: اعتبارسنجی داخلی در داتنت 10
در داتنت 10، مایکروسافت پشتیبانی داخلی برای اعتبارسنجی مبتنی بر ویژگیها را به Minimal APIها اضافه کرد. این قابلیت، که در حال حاضر در مرحله پیشنمایش (Preview) قرار دارد، امکان استفاده از ویژگیهای موجود در فضای نام System.ComponentModel.DataAnnotations را فراهم میکند. این ویژگیها شامل اعتبارسنجی رشتههای پرسوجو (Query Strings)، سرآیندها (Headers)، پارامترهای مسیر (Route Parameters) و بدنه درخواست (Request Body) میشوند.
برای استفاده از این قابلیت، ابتدا باید مدل داده را با ویژگیهای مناسب تعریف کنیم. به عنوان مثال:
public class CreateNoteRequest
{
[Filled]
public Guid Id { get; set; }
[Required]
public string Value { get; set; }
}در اینجا، ویژگی Required تضمین میکند که مقدار Value خالی نباشد. برای اعتبارسنجی Guid و اطمینان از غیرخالی بودن آن، یک ویژگی سفارشی به نام Filled ایجاد میشود:
[AttributeUsage(AttributeTargets.Property)]
public sealed class FilledAttribute : ValidationAttribute
{
public override bool IsValid(object? value)
{
if (value is not Guid guid)
{
return false;
}
return guid != Guid.Empty;
}
}برای فعالسازی اعتبارسنجی داخلی، باید خدمات مربوطه در ظرف تزریق وابستگی (DI Container) ثبت شوند:
builder.Services.AddValidation();
همچنین، تنظیمات خاصی در فایل پروژه (.csproj) مورد نیاز است:
<PropertyGroup>
<InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.Http.Validation.Generated</InterceptorsNamespaces>
</PropertyGroup>با این تنظیمات، نقطه پایانی به سادگی تعریف میشود و اعتبارسنجی به صورت خودکار انجام میشود:
app.MapPost("/v4/notes", (CreateNoteRequest request) =>
{
// ذخیره یادداشت در پایگاه داده
return Results.Ok();
});در صورت نیاز به غیرفعال کردن اعتبارسنجی برای یک نقطه پایانی خاص، میتوان از متد DisableValidation استفاده کرد:
app.MapPost("/v4/notes", (CreateNoteRequest request) =>
{
// ذخیره یادداشت در پایگاه داده
return Results.Ok();
}).DisableValidation();
چالشها و ملاحظات
اگرچه اعتبارسنجی داخلی در داتنت 10 پیشرفت چشمگیری محسوب میشود، اما همچنان در مرحله پیشنمایش است و مشکلاتی مانند عدم پشتیبانی کامل از اعتبارسنجی Guid و پیچیدگی تنظیمات اولیه وجود دارد. علاوه بر این، استفاده از اعتبارسنجی مبتنی بر مدل ممکن است بر عملکرد Minimal APIها تأثیر بگذارد، زیرا فرآیند اتصال مدل (Model Binding) پیچیدگیهای خاص خود را دارد. توسعهدهندگان باید بین سادگی و عملکرد تعادل برقرار کنند و بسته به نیاز پروژه خود، رویکرد مناسب را انتخاب کنند.
نتیجهگیری
اعتبارسنجی در Minimal APIها از روشهای دستی و پراکنده در نسخههای اولیه به یک سیستم منسجم و مبتنی بر مدل در داتنت 10 تکامل یافته است. این پیشرفتها، توسعه APIهای امنتر و قابل نگهداریتر را آسانتر کردهاند. با این حال، توسعهدهندگان باید از محدودیتهای فعلی، مانند تنظیمات پیچیده و تأثیرات احتمالی بر عملکرد، آگاه باشند. در آینده، انتظار میرود که با رفع این محدودیتها و سادهسازی فرآیندها، Minimal APIها به گزینهای حتی قدرتمندتر برای توسعه وب تبدیل شوند.