REST: محدودیت‌های معماری و شیوه‌های درست طراحی

REST: محدودیت‌های معماری و شیوه‌های درست طراحی

REST: محدودیت‌های معماری و شیوه‌های درست طراحی

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

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

REST: محدودیت‌های معماری و شیوه‌های درست طراحی

متأسفانه بسیاری از توسعه‌دهندگان HTTP و REST را با هم اشتباه می‌گیرند. HTTP یک پروتکل ارتباطی کلاینت–سرور است، درحالی‌که REST محدودیت‌هایی را دربارهٔ نحوهٔ گفت‌وگوی سرور و کلاینت مشخص می‌کند. در این بخش دو موضوع متفاوت را بررسی می‌کنیم: محدودیت‌های REST و شیوه‌های درست طراحی. رعایت محدودیت‌های REST باعث می‌شود اصول REST API را رعایت کنید؛ اما رعایت صرفاً Best Practiceها تضمین نمی‌کند که API شما با REST سازگار باشد.

محدودیت‌های REST

یک برنامهٔ وب باید منطق کسب‌وکار خود را بر مجموعه‌ای از Entityها—برای مثال Product—و عملیات ممکن روی آن‌ها—برای مثال بازیابی اطلاعات Product بر اساس شناسه—پیاده‌سازی کند. عملیات اصلی روی این Entityها باید حول چهار روش Create، Retrieve، Update و Delete که به اختصار CRUD نامیده می‌شوند طراحی شوند. این Entityها در REST «Resource» هستند و با قالبی مانند JSON، XML یا قالب‌های دیگر نمایش داده می‌شوند.

بنابراین کلاینت با استفاده از HTTP، یا حتی پروتکل‌های ارتباطی دیگری مانند gRPC یا SOAP، توابع CRUD روی سرور را فراخوانی می‌کند تا Representation یک Entity را روی سرور دست‌کاری کند. این بخش مفهوم Representational در REST را توضیح می‌دهد.

اما State Transfer یعنی چه؟ فرض کنید کلاینت ابتدا عملیات «ایجاد Product» را فراخوانی می‌کند. پس از ایجاد، حالت بعدی Product می‌تواند عملیاتی باشد که کلاینت قادر به فراخوانی آن است؛ برای مثال «بازیابی دادهٔ Product ایجادشده» یا «به‌روزرسانی دادهٔ Product». این انتقال میان حالت‌های قابل دسترس، مفهوم State Transfer را شکل می‌دهد.

REST فقط به این تعریف محدود نیست و بر شش Constraint استوار است:

  • تفکیک مسئولیت‌های کلاینت و سرور؛ کلاینت داده را نمایش می‌دهد و سرور آن را پردازش می‌کند.
  • نبود Session State یا Stateless بودن؛ کلاینت و سرور برای برقراری ارتباط به نگهداری State یکدیگر وابسته نیستند.
  • امکان Cache کردن Resourceها.
  • ارتباط یکنواخت با Resourceهای قابل شناسایی؛ در واژگان HTTP باید URL، Response دارای Body و Header وجود داشته باشد.
  • امکان افزودن Layerهای واسط، مانند Proxyها.
  • امکان درخواست یک قطعه Code از سرور توسط کلاینت و اجرای آن Code در سمت کلاینت.

در این کتاب عمدتاً چهار مورد اول را در طراحی REST APIها به‌کار خواهیم گرفت.

شیوه‌های درست REST

پیش‌تر وعده دادم به URI و URL بازگردیم. اکنون شیوه‌های مناسب تعریف آن‌ها در REST را بررسی می‌کنیم.

Base URL

ابتدا Base URL را تعیین می‌کنیم. Base URL ریشهٔ همهٔ Endpointهای HTTP است. برای یک وب‌سایت معمولی استفاده از Domain دقیق کسب‌وکار همراه Path و Parameterهای طولانی قابل قبول است، اما برای REST APIها URLهای ساده توصیه می‌شوند. فرض کنید شرکت شما وب‌سایت فروش Product دارد و همان قابلیت‌ها را از طریق REST API ارائه می‌کند. نمونهٔ زیر برای API مناسب نیست:

https://www.mycompany.com/home/services/rest

برای API بهتر است نشانی معنادارتر و ساده‌تری مانند این انتخاب شود:

https://api.mycompany.com

Media Type

قالب JSON همراه Header زیر توصیه می‌شود:

Content-Type: application/json

این گزینه رایج، عملی و کم‌هزینه است. XML نیز قابل استفاده است، اما JSON معمولاً به دلیل سادگی Syntax و همچنین Performance (کارایی) بهتر در Serialization/Deserialization ترجیح داده می‌شود. Serialization فرایندی است که یک Data Structure را به قالبی قابل ذخیره یا انتقال تبدیل می‌کند و Deserialization مسیر معکوس را انجام می‌دهد.

فرض کنید ساختار دادهٔ Product شامل شناسه، نام و توضیح باشد. Listing 1-1 نمایش JSON آن را نشان می‌دهد.

Listing 1-1. Serialization ساختار Product به JSON

{
          "Id": 1,
          "name": "My product name,"
          "description": "My product description"
    }

Listing 1-2 همان ساختار را در XML نشان می‌دهد.

Listing 1-2. Serialization ساختار Product به XML

<?xml version="1.0" encoding="utf-8"?>
    <product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Id>1</Id>
      <Name>My product name</Name>
      <Description>My product description</Description>
    </product>

JSON در مقایسه با XML کم‌حجم‌تر و خواناتر است و Verbosity کمتری دارد.

موضوع دیگری که مستقیماً Constraint مربوط به HTTP نیست، استفاده از Content-Type مناسب برای کاهش برخی خطرها مانند Cross-Site Request Forgery (CSRF) است. در چنین حمله‌ای Code سمت کلاینت، مانند JavaScript، ممکن است عملیات ناخواسته‌ای را در مرورگر کاربر اجرا کند. وادار کردن تبادل داده به Serialization مشخصی مانند JSON یا XML می‌تواند بخشی از کنترل‌های محافظتی باشد. برای مطالعهٔ بیشتر می‌توان به مطلب CSRF در وب‌سایت OWASP مراجعه کرد.

نام‌گذاری URL

این موضوع را با جدیت دنبال می‌کنم، زیرا اگر URLها درست نوشته شوند، بدون افزودن کلمات غیرضروری می‌توان از روی خود نشانی فهمید چه کاری انجام می‌دهند. فرض کنید می‌خواهید فهرست کامل Productهای Database را بازیابی کنید. نوشتن URL به شکل زیر مناسب نیست:

(GET) /getAllProducts

این URL عملاً یک جمله است. شکل ساده‌تر و روشن‌تر چنین است:

(GET) /products

Verb برابر GET از قبل عمل را بیان می‌کند و نیازی نیست واژهٔ get داخل URL تکرار شود. استفاده از اسم جمع products کافی است.

اگر فقط ده Product بخواهیم، این شکل را ننویسید:

(GET) /getSomeProducts?limit=10

بلکه بنویسید:

(GET) /products?limit=10

برای ایجاد Product نیز از این استفاده کنید:

POST /products

نه این‌ها:

POST /products/create
    POST /createProduct

در منطق State Transfer، Response عملیات POST می‌تواند Status برابر 201 Created و ID منبع ایجادشده را در Header یا Payload برگرداند. سپس برای بازیابی Product ایجادشده از این URL استفاده می‌شود:

GET /products/{id}

و نه:

GET /getProduct?id={id}

همین منطق برای ویرایش و حذف نیز ادامه پیدا می‌کند. مجموعهٔ CRUD به این صورت است:

  • Create: POST /products
  • Retrieve: GET /products/{id}
  • Update: PUT یا PATCH /products/{id}
  • Delete: DELETE /products/{id}

این الگو برای Resourceهای مرتبط هم صدق می‌کند. اگر Product به Category وابسته باشد و Product متعلق به Category مشخصی باشد، برای دسترسی یا دست‌کاری Productهای آن Category بهتر است Path چنین باشد:

/categories/{categoryId}/products

برای Product مشخص در Category مشخص:

/categories/{categoryId}/products/{productId}

در این حالت CRUD چنین می‌شود:

  • Create: POST /categories/{categoryId}/products
  • Retrieve: GET /categories/{categoryId}/products/{productId}
  • Update: PUT یا PATCH /categories/{categoryId}/products/{productId}
  • Delete: DELETE /categories/{categoryId}/products/{productId}

البته می‌توان Product را بدون دسترسی به Category مدیریت کرد. اما اگر منطق کسب‌وکار شما لازم می‌داند پیش از دست‌کاری Product بررسی شود که به Category خاصی تعلق دارد، این ساختار Path از استفادهٔ Query Parameter مناسب‌تر است. بعداً دوباره به این موضوع برمی‌گردیم. قراردادن ID در URL Path همان چیزی است که Routing نامیده می‌شود و در ASP.NET Core، categoryId و productId Route Parameter هستند.

API Versioning (نسخه‌بندی API)

گاهی API به‌سرعت از نظر قابلیت‌ها یا قراردادهای سرویس تکامل می‌یابد و تغییر در Data Structureهای مبادله‌شده میان کلاینت و سرور می‌تواند رفتار قبلی را بشکند. کلاینت‌ها معمولاً با سرعت کمتری تغییر می‌کنند و API نباید برنامه‌های موجود را از کار بیندازد. یکی از راه‌حل‌ها API Versioning است.

یک روش رایج، قرار دادن نسخه در URL است؛ یعنی برای هر نسخهٔ API نشانی جداگانه‌ای داشته باشیم:

https://api.mycompany.com/v1/categories
    https://api.mycompany.com/v2/categories

بعداً در کتاب این روش را با جزئیات بیشتری بررسی می‌کنیم.

روش دیگر، تعیین نسخه با Header سفارشی است. HTTP Header استاندارد خاصی برای نسخهٔ API تعریف نکرده است؛ بنابراین می‌توان Header خود را ساخت:

GET https://api.mycompany.com/categories
    X-API-Version: 1

من به‌ندرت Versioning مبتنی بر Header را استفاده می‌کنم، اما گزینه‌ای معتبر است و در فصل ۴ نمونه‌هایی از آن خواهیم دید.

روش سوم، Media Type Versioning است که از Headerهای Accept/Content-Type برای تعیین نسخه استفاده می‌کند. این روش بسیار کمتر دیده می‌شود و در این کتاب وارد جزئیات آن نمی‌شوم؛ مستندات Microsoft دربارهٔ API Design اطلاعات بیشتری ارائه می‌کند.

مستندسازی API

مستندسازی API یکی از موضوعات مهم و تقریباً مورد توافق همه است. منظور این است که در یک Endpoint اختصاصی، صفحهٔ HTML، فایل YAML یا JSON، همهٔ URLهایی که API ارائه می‌کند—از جمله Versionها—به همراه Parameterهای ورودی، ساختار دادهٔ خروجی، HTTP Status Codeها و سایر قراردادهای مصرف API منتشر شوند. هدف این است که کلاینت بداند چگونه API را درست استفاده کند.

برای این کار Specificationای به نام OpenAPI وجود دارد. مجموعه ابزارهای شناخته‌شده‌ای که این Specification را پیاده‌سازی می‌کنند Swagger نام دارند. بعداً ابزارهای Swagger برای ASP.NET Core را بررسی خواهیم کرد. مشخصات OpenAPI نیز در وب‌سایت OpenAPI Initiative منتشر می‌شود.

جمع‌بندی

این فصل طولانی و پُرمطلب بود، اما در ادامه بارها به مطالب آن بازمی‌گردیم و دانسته‌های این فصل را در عمل به کار می‌گیریم. این فصل مقدمه‌ای ضروری برای پیاده‌سازی درست APIها است، زیرا بر RFCها تکیه دارد؛ RFCها مرجعی هستند که به فهم چیستی HTTP و نحوهٔ کار آن کمک می‌کنند.

پیش از ادامهٔ کتاب لازم بود Headerها، Verbها، Status Codeها و Parameterهایی را که برای فراخوانی URLها استفاده می‌شوند بشناسید. همچنین Best Practiceهای رایج توسعهٔ REST API را معرفی کردم. این Best Practiceها الزاماً بر RFC بنا نشده‌اند، اما در جامعهٔ توسعه‌دهندگان در سراسر جهان پذیرفته شده‌اند. در فصل بعد روی ASP.NET Core 8 تمرکز خواهیم کرد.

تصاویر منبع مرتبط با این بخش

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

منبع: 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