نوید خلیلیان
نوید خلیلیان
خواندن ۵ دقیقه·۲ سال پیش

افزایش پرفورمنسAPI با استفاده از FastEndpoints


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

معرفی FastEndPoints

طبق توضیح سایت طراح این پکیج می تونیم FastEndpoints را بعنوان جایگزین MVC و Netcore Minimal APIs معرفی کنیم. در واقع طبق گفته های سازنده و بنچ مارک انجام شده این پکیج توانایی هندل کردن 35K درخواست بیشتر در ثانیه داره.

درواقع این پروژه از Minimal API در Dotnet 6 الگو برداری کرده و سعی میکنه مباحث Clean code را جهت نگهداری کد تمیزتر در پروژه پوشش بده.

یکی از دلایل تمیزتر بودن کد ها با FastEndpoints به دلیل جایگزین کردن الگوی MVC با REPR هست.

معرفی کوتاه الگوی REPR

[R]EQUEST [E]ND[P]OINTS [R]ESPONSE

در واقع MVC یک رویکرد سنتی بود که مایکروسافت از اون بعنوان ترویج دات نت استفاده کرد. یکی از مشکلاتی که MVC داره پیچیده شدن پروژه ها و خارج شدن از چهارچوب استاندارد مبتنی بر REST هست.

الگوی REPR هر Endpoint را به سه بخش تقسیم میکنه:

  1. Request

داده ای که API انتظار دارد بعنوان ورودی بگیره

2. Endpoint

منطقی که در بدنه API پیاده سازی شده

3. Response

خروجی که API برمیگردونه.

این الگو اصل Single Responsibility از SOLID را مبنای پیاده سازی قرار داده و فقط ورودی و خروجی برای اون اهمیت داره.بعضی موافق API هیچ ورودی یا خروجی نداره و فقط یک کد Status برمیگردونه.

بریم یدونه مثال در ادامه باهم ببینیم

استفاده از FastEndpoints در DotNet 6.0

اول یدونه پروژه DotNet 6.0 می سازیم و بسته های زیر رو نصب میکنیم

dotnet add package FastEndpoints // The actual library dotnet add package FastEndpoints.Swagger // For Swagger suppo

بسته اول رو برای استفاده از FastEndpointsو دومی رو برای کانفیگ Swagger نصب کردیم.

پروژه قراره یدونه API داشته باشه که لیست مشتریان رو برمیگردونه. اول یدونه پوشه به اسم Features توی Root پروژه میسازیم و درون اون یدونه پوشه دیگه به اسم Customers ایجاد میکنیم.قراره API های ما که توسط فرانت استفاده میشن توی این پوشه قرار بگیره.

ساختار پروژه شبیه این میشه

Solution - Project -- Features --- Customers ---- GetAllCustomers ----- Models.cs ----- Endpoint.cs ----- (Mapper.cs)

همونطور که میدونید با توجه به استاندارد نام گذاری از Get ابتدای نام Name space استفاده کردیم.

خوب بریم فایل Program.cs رو به این شکل تغییر بدیم:

global using FastEndpoints;
global using FastEndpoints.Validation;
var builder = WebApplication.CreateBuilder();
builder.Services.AddFastEndpoints();
var app = builder.Build();
app.UseAuthorization();
app.UseFastEndpoints();
app.Run();

حالا کلاس request و response را میسازیم

public class Request
{
public Guid Id { get; init; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string CustomerNo { get; set; }
}
public class Response
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string CustomerNo { get; set; }
}

حالا می تونیم کلاس Endpoint رو بسازیم:

public class Endpoint : EndpointWithoutRequest<List<Response>>
{
public override void Configure()
{
Verbs(Http.GET);
Routes(&quot/Customers/GetAllCustomers&quot);
AllowAnonymous();
}
public override async Task HandleAsync(CancellationToken ct)
{
var customers = new List<Response>();
customers.Add(
new Response
{
CustomerNo = &quot1&quot,
FirstName = &quotNAVID&quot,
LastName = &quotKHALILIAN&quot,
});
customers.Add(
new Response
{
CustomerNo = &quot2&quot,
FirstName = &quotALI&quot,
LastName = &quotKHALILIAN&quot,
});
await SendAsync(customers, cancellation: ct);
}
}

کلاس Endpoint از اینترفیس EndpointWithoutRequest ارث بری کرده و خروجی اون لیستی از کلاس های response هست. اینجا ما از EndpointWithoutRequest ارث بری کردیم چون متد ما ورودی نداره.حالا باید دو قسمت Configure و Handle رو مطابق چیزی که توضیح دادیم پیاده سازی کنیم.

در کل 4 تا اینترفیس برای ارث بری وجود داره:

  1. Endpoint<TRequest>

از این اینترفیس وقتی که تایپ ورودی مشخصه ولی خروجی تایپ مشخصی نداره مثل Generic استفاده میشه

  1. Endpoint<TRequest,TResponse>

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

  1. EndpointWithoutRequest

از این اینترفیس وقتی که ورودی نداریم و تایپ خروجی هم میتونه Generic باشه استفاده میکنیم

  1. EndpointWithoutRequest<TResponse>

از این اینترفیس زمانی که تایپ ورودی مشخص نیست ولی خروجی مشخصه استفاده میکنیم

نکته:

اگر API ورودی و خروجی نداشته باشه میتونه به شکل زیر پیاده سازی بشه:

public class MyEndpoint : Endpoint<EmptyRequest,EmptyResponse> { }

وقتش رسیده که بریم Swagger رو کانفیگ کنیم

محتوای فایل program.cs رو به شکل زیر تغییر میدیم:

global using FastEndpoints;
global using FastEndpoints.Validation;
using FastEndpoints.Swagger;
var builder = WebApplication.CreateBuilder();
builder.Services.AddFastEndpoints();
builder.Services.AddSwaggerDoc();
var app = builder.Build();
app.UseAuthorization();
app.UseFastEndpoints();
app.UseOpenApi();
app.UseSwaggerUi3(c => c.ConfigureDefaults());
app.Run();

اگر همه چیز به شکل صحیح کانفیگ شده باشه حالا میتونیم API رو با کمک Swagger تست کنیم. من فرض رو بر این گذاشتم که مخاطب این مقاله قبلا با Swagger کار کرده و توضیحات اضافه نمیدم. فقط تصویر Swagger رو پیوست کردم که خروجی رو با کار خودتون مقایسه کنید.

حالا که تا اینجای موضوع رو بررسی کردیم بهتره یه نگاهی هم به موضوع Dependency Injection موقع استفاده از FastEndpoints بندازیم.

بیایید یدونه اینترفیس به اسم ICustomerService و یدونه سرویس به اسم CustomerService بسازیم

public interface ICustomerService
{
Task<List<Response>> GetCustomers();
} public class CustomerService : ICustomerService
{
public Task<List<Response>> GetCustomers()
{
return Task.FromResult(new List<Response>
{
new Response { CustomerNo = &quot1&quot, FirstName = &quotNAVID&quot, LastName = &quotKHALILIAN&quot},
new Response { CustomerNo = &quot2&quot, FirstName = &quotALI&quot, LastName = &quotKHALILIAN&quot},
});
}
}

حالا سرویس رو رجیستر میکنیم

builder.Services.AddScoped<ICustomerService, CustomerService>();

حالا کلاس Endpoint رو تغییر میدیم و از سرویسی که ساختیم استفاده میکنیم

public class Endpoint : EndpointWithoutRequest<List<Response>>
{
private readonly ICustomerService _customerService;
public Endpoint(ICustomerService customerService)
{
_customerService = customerService;
}
public override void Configure()
{
Verbs(Http.GET);
Routes(&quot/Customers/GetAllCustomers&quot);
AllowAnonymous();
}
public override async Task HandleAsync(CancellationToken ct)
{
var customers = await _customerService.GetCustomers();
await SendAsync(customers, cancellation: ct);
}
}

همونطور که متوجه شدید بحث Dependency Injection مثل قبل قابل استفاده هست و نحوه استفاده از اون در FastEndpointتغییری نسبت به MVC نداشته.

نتیجه گیری:

به نظر من کد های نوشته شده با FastEndpoints نسبت به MVC واقعا تمیز تر و خوانا تره. البته هنوز از این پکیج توی پروژه واقعی استفاده نکردم و نتونستم پرفورمنس اون رو توی پروژه تجاری بررسی کنم ولی طبق بررسی که روی سایت توسعه دهنده و گیتهاب اون داشتم مستندات واقعا کامل و جامع هست. همچنان پروژه داره رشد میکنه و شاید در آینده این مکانیزم جایگزین MVC بشه. میتونید Github پروژه رو بررسی کنید و اگه خوشتون اومد لایک کنید که بیشتر مورد توجه قرار بگیره و به رشد اون کمک کنه.

امیدوارم این مقاله برای شما مفید بوده باشه.

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

موفق و پیروز باشید.


fastendpointsperformanceapimvcنوید خلیلیان
Full Stack Developer - Dev Ops
شاید از این پست‌ها خوشتان بیاید