مسیریابی در ASP.NET Core 8 و RouteGroups

مسیریابی در ASP.NET Core 8 و RouteGroups

مسیریابی در ASP.NET Core 8 و RouteGroups

منبع: Coding Clean, Reliable, and Safe REST APIs with ASP.NET Core 8 — Anthony Giretti

اعتبار ترجمه: ترجمه با کمک هوش مصنوعی

فصل ۴: مبانی REST APIهای تمیز — Routing و RouteGroups

اکنون نوبت توسعهٔ API است. در این فصل رایج‌ترین عملیات Minimal API در ASP.NET Core 8 را بررسی می‌کنیم؛ قابلیت‌هایی که تقریباً در هر API به آن‌ها نیاز دارید. URIهای قابل فهم، Validation پارامترهای ورودی، Object Mapping، انتخاب HTTP Status Code مناسب، Download و Upload فایل، Streaming، API Versioning، Documentation و CORS از موضوعات اصلی‌اند.

  • Routing با ASP.NET Core 8
  • Parameter Binding
  • Input Validation
  • Object Mapping
  • CRUD و HTTP Statusها
  • Download و Upload فایل
  • Streaming Content
  • Handling CORS
  • API Versioning
  • API Documentation

doi.org/10.1007/978-1-4842-9979-1_4

Routing با ASP.NET Core 8

در فصل ۱ با اصول HTTP و REST آشنا شدیم. اکنون URLهایی را که بر اساس آن اصول طراحی می‌شوند با Routing در ASP.NET Core 8 پیاده‌سازی می‌کنیم. دو روش را می‌بینیم: Route مستقل برای یک Endpoint و RouteGroups برای به‌اشتراک‌گذاری قسمت‌های قابل استفادهٔ مجدد Route میان چند Endpoint.

ASP.NET Core Routing

Routing توانایی پاسخ‌دادن به HTTP Request کلاینت است. ASP.NET Core با Pattern Matching درخواست را تحلیل می‌کند تا Endpoint متناظر با URL را پیدا کند. اگر Endpoint پیدا نشود، Response برابر 404 Not Found برگردانده می‌شود.

تصویر منبع — صفحهٔ 104Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 104.
شکل ۴-۱. Routing در ASP.NET Core

لازم نیست وارد جزئیات Pattern Matching شویم. تمرکز ما روی Mapping Verbهای HTTP به Route و اعمال Constraint روی Route Parameterها است.

تنظیم HTTP Verb درست

ASP.NET Core 8 برای هر Verb اصلی Method اختصاصی Mapping دارد. هر Method معمولاً Route Pattern و Delegate مربوط به Handler را می‌گیرد.

جدول ۴-۱. Verbهای HTTP و Methodهای مرتبط
HTTP VerbMethod
GETMapGet
POSTMapPost
PATCHMapPatch
PUTMapPut
DELETEMapDelete
Verbهای دیگرMethod اختصاصی ندارند

نمونهٔ Signature برای POST:

app.MapPost("/yourRouteName", () =>  /*  Do action */);

Verbهایی مانند OPTIONS، TRACE و HEAD Method اختصاصی ندارند، اما می‌توان با MapMethods چند Verb را روی یک Route تعریف کرد:

app.MapMethods("/routeName", new List<string> { "OPTIONS",
    "HEAD", "TRACE" }, () => { /* Do action */});

Delegate می‌تواند Parameter دریافت کند. در ادامه و در فصل بعد، به‌خصوص هنگام Custom Parameter Binding، نمونه‌های بیشتری خواهیم دید.

نوشتن Routeها

نوشتن Route ساده است؛ نوشتن Route خوب و سازگار با REST نیازمند دقت در Readability است. Best Practice این است که Routeها معنی‌دار باشند. این موضوع برای REST API تمیز بسیار مهم است. Route Parameterها به‌صورت خودکار به Parameterهای Lambda Function مربوط به Endpoint Bind می‌شوند. Route بدون Parameter نیز یک Route ثابت است.

تصویر منبع — صفحهٔ 106Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 106.
شکل ۴-۲. Routing پایه

برای مثال، /countries/{id} یک Country را از میان مجموعهٔ Countries بر اساس ID مشخص می‌کند و /countries کل فهرست Countries را نشان می‌دهد.

ASP.NET Core 8 اجازه می‌دهد Primitive Typeهای متعددی به‌عنوان Route Parameter استفاده شوند، از جمله bool، byte، sbyte، short، ushort، int، uint، long، ulong، char، double، decimal و float.

Typeهای پیچیده‌تری که به String قابل Parse شدن هستند، مانند DateTime و Guid، نیز قابل استفاده‌اند.

Listing 4-1. Routeهایی با DateTime و Guid

var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();

    app.MapGet("/date/{date}", (DateTime date) => date.ToString());

    app.MapGet("/uniqueidentifier/{id}", (Guid id) =>
    id.ToString());
    app.Run();
تصویر منبع — صفحهٔ 108Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 108.
شکل ۴-۳. تلاش برای Bind کردن String به‌جای Integer و دریافت 400 Bad Request

اگر Route وجود داشته باشد ولی HTTP Verb با آن Match نشود، 405 Method Not Allowed برگردانده می‌شود. Listing 4-2 یک Route مشترک برای PUT و PATCH را نشان می‌دهد.

Listing 4-2. Endpoint با PUT و PATCH روی یک Route

app.MapMethods("/users/{userId}", new List<string> { "PUT",
    "PATCH" }, (int userId, HttpRequest request) =>
    {
        var id = request.RouteValues["id"];
        var lastActivityDate = request.Form["lastactivitydate"];
        /* code to update user */
    });

فراخوانی همین Route با POST به 405 Method Not Allowed منجر می‌شود.

تصویر منبع — صفحهٔ 109Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 109.
تصویر منبع — صفحهٔ 109Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 109.
شکل ۴-۴. Verb اشتباه و Response برابر 405 Method Not Allowed

اگر اصلاً Endpoint متناظر با Route پیدا نشود، بدون توجه به Verb، پاسخ 404 Not Found داده می‌شود.

شکل ۴-۵. Route ناموجود و Response برابر 404 Not Found
جدول ۴-۲. رفتار Routing و Responseهای ممکن
رفتارResponse
Route Match است و Verb درست است، اما Parameter Binding شکست می‌خورد.400 Bad Request
Route Match است اما Verb نادرست است.405 Method Not Allowed
Route وجود ندارد.404 Not Found
Route و Verb درست‌اند و Binding موفق است.2XX Success

ASP.NET Core 8 با این رفتارها به شما کمک می‌کند Routeهای REST مناسب بنویسید و نوع خطا را تشخیص دهید.

Route Constraintها

می‌توان روی Route Parameterها Constraint تعریف کرد تا تنها مقادیر مطابق Pattern مشخص Route را Match کنند. Constraint با Validation ورودی یکسان نیست؛ Validation را جداگانه بررسی خواهیم کرد.

Listing 4-3 Constraintای روی provinceId تعریف می‌کند که باید Integer باشد.

تصویر منبع — صفحهٔ 111Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 111.

Listing 4-3. Constraint از نوع int برای provinceId

app.MapGet("/provinces/{provinceId:int}", (int provinceId) =>
    $"ProvinceId {provinceId}");

Syntax عمومی به این شکل است:

{ParameterName:DataType}

اگر Constraint رعایت نشود، ASP.NET Core 8 پاسخ 404 Not Found می‌دهد، نه 400.

شکل ۴-۶. ارسال String برای Route دارای Constraint عددی و دریافت 404

علت منطقی است: Constraint بخشی از هویت Route محسوب می‌شود؛ اگر آن شرط برقرار نباشد، Route اساساً Match نشده است. Constraint پیش از Parameter Binding بررسی می‌شود، بنابراین خطا 400 نیست.

به همین دلیل توصیه نمی‌کنم Route Constraint را جایگزین Parameter Validation کنید، چون در آن صورت تشخیص این‌که 404 ناشی از Route اشتباه است یا Constraint نامعتبر دشوار می‌شود. Constraint نوعی مانند Integer یا DateTime می‌تواند منطقی باشد، زیرا Type بخشی از Contract Route است؛ اما Constraintهای Validationمحور باید با احتیاط استفاده شوند.

جدول ۴-۳. Route Constraintهای موجود در ASP.NET Core 8
ConstraintPatternتوضیح
int{p:int}Integer
bool{p:bool}Boolean
datetime{p:datetime}DateTime
decimal{p:decimal}Decimal
double{p:double}Double
float{p:float}Float
guid{p:guid}Guid
long{p:long}Long
minlength{p:minlength(n)}حداقل Length
maxlength{p:maxlength(n)}حداکثر Length
length{p:length(n)}Length دقیق
length(min,max){p:length(n1, n2)}بازهٔ Length
min{p:min(n)}حداقل مقدار Integer
max{p:max(n)}حداکثر مقدار Integer
range{p:range(n1, n2)}بازهٔ Integer
alpha{p:alpha}حروف Alphabetic بدون حساسیت به Case
regex{p:regex(\...)}Regular Expression
required{p:required}Parameter غیر Null

از minlength به بعد، بسیاری از Constraintها عملاً شبیه Validation عمل می‌کنند و نویسنده استفاده از آن‌ها را برای Validation توصیه نمی‌کند. ترجیح او محدود کردن Constraint به Type مورد انتظار است.

Constraintها را می‌توان Chain کرد:

app.MapGet("/provinces/{provinceId:int:max(12)}", (int
    provinceId) => $"ProvinceId {provinceId}");

در این مثال نخست Integer بودن و سپس حداکثر مقدار ۱۲ بررسی می‌شود؛ اما بخش دوم Parameter Validation است و بهتر است مسئولیت Routing نباشد. ASP.NET Core امکان Custom Route Constraint را نیز دارد، ولی نویسنده آن را Practice خوبی نمی‌داند. اصل مهم این است که Parameter Validation طبق RFCها باید به 400 Bad Request منجر شود.

RouteGroups

وقتی Endpointهای زیادی دارید که به Functionality یکسان تعلق دارند—مثلاً مجموعه Endpointهای مدیریت Countries—Route Grouping در ASP.NET Core مفید است. این قابلیت اجازه می‌دهد Routeها و حتی پیاده‌سازی آن‌ها را در Function جدا Isolate کنید و Ruleهای مشترک، مانند URL Trunk یا بعداً Authorization مشترک، را روی Group اعمال کنید.

سه Endpoint زیر را در نظر بگیرید که همگی Trunk برابر /countries دارند: فهرست Countries، دریافت Country با ID، و دریافت زبان‌های Country. Listing 4-4 آن‌ها را در Extension Methodای به نام GroupCountries روی RouteGroupBuilder گروه‌بندی می‌کند.

Listing 4-4. سه Endpoint مربوط به Countries

namespace AspNetCore8MinimalApis.RouteGroups;
    public static class MyGroups
    {
        public static RouteGroupBuilder GroupCountries(this
         RouteGroupBuilder group)
        {
            var countries = new string[]
    
            {
                "France",
                "Canada",
                "USA"
            };

            var languages = new Dictionary<string, List<string>>()
            {
                { "France", new List<string> { "french" } },
                { "Canada", new List<string> { "french",
                 "english" } },
                { "USA", new List<string> { "english",
                 "spanish" } }
            };

            group.MapGet("/", () => countries);
            group.MapGet("/{id}", (int id) => countries[id]);
            group.MapGet("/{id}/languages", (int id) =>
            {
                var country = countries[id];
                return languages[country];
            });

            return group;
        }
    }

سپس این Group در ASP.NET Core Pipeline و در Program.cs ثبت می‌شود.

Listing 4-5. ثبت Route Group مربوط به Countries

using AspNetCore8MinimalApis.RouteGroups;

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();

    app.MapGroup("/countries").GroupCountries();

    app.Run();

پیش از ثبت Group، Trunk مشترک با MapGroup تعریف می‌شود. همهٔ Routeها آن را به ارث می‌برند و URLهای نهایی عبارت‌اند از:

  • /countries
  • /countries/{id}
  • /countries/{id}/languages

در Endpoint اول Slash تنها را می‌توان نگه داشت یا حذف کرد؛ نویسنده به‌عنوان Convention آن را نگه می‌دارد.

Route Grouping فراتر از Trunk مشترک است و می‌تواند Constraint مشترک را نیز بین Endpointها Reuse کند. دو Endpoint آخر هر دو {id} دارند؛ در Listing بعدی این قسمت با یک Group داخلی مشترک می‌شود.

منبع: Coding Clean, Reliable, and Safe REST APIs with ASP.NET Core 8 — Anthony Giretti.

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

امتیاز کاربران به این مقاله

☆☆☆☆☆

0 نفر امتیاز داده اند. میانگین: 0.0 از 5

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

0 / 500

اطلاعات تماس

  • آدرس:اصفهان-خیابان ام کلثوم غربی - بعد خیابان تخم چی - بیست متر بعد از پیتزا ننه شب - کوچه تعمیر گاه سمار زغالی - پلاک 354 - درب مشکی - طبقه هفتم
  • آدرس ایمیل:najafzade@gmail.com
  • وب سایت:http://www.a00b.com/
  • تلفن ثابت:(+98)9131253620
  • تلفن همراه:09131253620