فصل نهم: طراحی و توسعه API ها 🚀
واسطهای برنامهنویسی کاربردی (Application Programming Interfaces یا APIها) در این روزها بهشدت مهم و حیاتیتر از هر زمان دیگری هستند. APIها برای اتصال دولتها و مؤسسات بهمنظور اشتراکگذاری دادهها و همکاری در مسائل تجاری و دولتی استفاده میشوند. آنها بین کلینیکهای پزشکی و بیمارستانها برای اشتراک لحظهای اطلاعات بیماران بهکار میروند.
شما هر روز بدون اینکه متوجه شوید از APIها استفاده میکنید؛ برای مثال، هنگام اتصال به ایمیلها یا زمانی که با همکاران و مشتریان خود در پلتفرمهایی مثل Microsoft Teams، Microsoft Azure، Amazon Web Services و Google Cloud Platform همکاری میکنید، در حال استفاده از API هستید.
هر بار که با کسی چت یا تماس ویدیویی برقرار میکنید، چه با کامپیوتر چه با تلفن همراه، در حال استفاده از API هستید. هنگام پخش زنده کنفرانسهای ویدیویی، ورود به چت پشتیبانی فنی وبسایتها یا استریم موسیقی و ویدیوهای مورد علاقهتان، همگی نمونههایی از استفاده از API هستند.
بنابراین، بهعنوان یک برنامهنویس، ضروری است که بهخوبی با مفهوم APIها، طراحی، توسعه، امنیت و استقرار آنها آشنا باشید.
در این فصل، درباره اینکه API چیست، چه مزایایی دارد و چرا لازم است درباره آن یاد بگیریم صحبت خواهیم کرد. همچنین موضوعات زیر را بررسی میکنیم:
- API proxies
- راهنماهای طراحی و توسعه API
- طراحی API با استفاده از RAML
- مستندسازی API با Swagger
موضوعات پوشش داده شده در این فصل: 📚
- API چیست؟
- API proxies
- راهنماهای طراحی API
- طراحی API با RAML
- توسعه و مستندسازی API با Swagger
مهارتهایی که در این فصل بهدست خواهید آورد: 🛠
- درک مفهوم API و اهمیت یادگیری آن
- شناخت API proxies و دلایل استفاده از آنها
- آشنایی با اصول طراحی در زمان ساخت APIهای خود
- طراحی API با استفاده از RAML
- مستندسازی API با Swagger
در پایان این فصل، شما با مبانی طراحی خوب API آشنا خواهید شد و دانش لازم برای ارتقاء مهارتهای API خود را بهدست میآورید. مهم است که ابتدا درک کنیم API چیست، بنابراین از همینجا شروع میکنیم. اما پیش از آن، اطمینان حاصل کنید که پیشنیازهای فنی زیر را برای بهرهبرداری کامل از این فصل پیادهسازی کردهاید.
پیشنیازهای فنی: 💻
برای ایجاد یک API در این فصل، از فناوریهای زیر استفاده خواهیم کرد:
- Visual Studio 2019 Community Edition یا نسخههای بالاتر
- Swashbuckle.AspNetCore 5 یا بالاتر
- Swagger (https://swagger.io)
- Atom (http://atom.io)
- API Workbench by MuleSoft
API چیست؟ 🤔
APIها کتابخانههای قابل استفاده مجدد هستند که میتوانند بین برنامههای مختلف به اشتراک گذاشته شوند و از طریق سرویسهای REST در دسترس باشند (در این حالت به آنها RESTful APIs گفته میشود).
REST (Representational State Transfer) روشی معماری است که توسط Roy Fielding در سال ۲۰۰۰ معرفی شد. REST از محدودیتهایی تشکیل شده که هنگام ایجاد سرویسهای REST باید در نظر گرفته شوند. این محدودیتها شامل شش بخش زیر هستند:
-
Uniform Interface (رابط یکنواخت):
برای شناسایی منابع و مدیریت آنها از طریق نمایش استفاده میشود. پیامها خود توصیفگر هستند و از Hypermedia بهره میبرند. HATEOAS (Hypermedia as the Engine of Application State) برای اطلاعات مربوط به عملیات بعدی قابل انجام توسط کلاینت بهکار میرود. -
Client-Server (کلاینت-سرور):
این محدودیت از طریق پنهانسازی اطلاعات (Encapsulation) عمل میکند. فقط APIهایی که باید توسط کلاینت استفاده شوند قابل مشاهدهاند و سایر APIها مخفی میمانند. یک RESTful API باید مستقل از بخشهای دیگر سیستم باشد و اتصال سست (loosely coupled) داشته باشد. -
Stateless (بیحالت):
APIهای RESTful هیچ وضعیت یا سابقهای ذخیره نمیکنند. اگر کلاینت به سابقه نیاز داشته باشد، باید تمام اطلاعات لازم را در هر درخواست ارسال کند. -
Cacheable (قابل کش شدن):
منابع باید قابلیت ذخیرهسازی موقت (Cache) داشته باشند. این باعث میشود دسترسی سریعتر و بار روی سرور کمتر شود و در نتیجه سرعت API بالاتر میرود. -
Layered System (سیستم لایهای):
هر لایه باید فقط یک وظیفه مشخص داشته باشد. هر مؤلفه تنها باید از بخشهایی که نیاز دارد آگاهی داشته باشد و نباید از بخشهای دیگر سیستم مطلع باشد. -
Optional Executable Code (کد اجرایی اختیاری):
این محدودیت اختیاری است و به سرورها اجازه میدهد بهطور موقت کد اجرایی به کلاینت منتقل کنند تا قابلیتهای جدید اضافه یا سفارشیسازی شوند.
بنابراین هنگام طراحی یک API، باید فرض کنید کاربر نهایی میتواند هر سطحی از تجربه برنامهنویسی داشته باشد. او باید بتواند بهراحتی API را دریافت کرده، مستندات آن را مطالعه کند و بلافاصله از آن استفاده کند.
نگران ایجاد API کاملاً بینقص نباشید؛ چراکه APIها بهمرور تکامل پیدا میکنند. اگر با APIهای مایکروسافت کار کرده باشید، میدانید که آنها مرتباً بهروزرسانی میشوند. APIهایی که قرار است در آینده حذف شوند، معمولاً با Annotation (یادداشت) مشخص میشوند تا به کاربر هشدار دهند از ویژگی یا متدی خاص استفاده نکند. در نهایت، وقتی ویژگیها منسوخ شدند، با برچسب Obsolete علامتگذاری و سپس حذف میشوند. این روند به کاربران فرصت میدهد برنامههای خود را بهروز کنند.
چرا از سرویسهای REST برای دسترسی به API استفاده کنیم؟ 💡
بسیاری از شرکتها از طریق ارائه APIهای آنلاین و پولی یا رایگان سودهای کلانی کسب میکنند. RESTful APIها میتوانند یک دارایی ارزشمند باشند.
وبسایت Rapid API (https://rapidapi.com/) مجموعهای از APIهای رایگان و پولی را ارائه میدهد. APIهای شما میتوانند بهصورت دائمی و مقیاسپذیر روی فضای ابری در دسترس باشند و یا رایگان یا اشتراکی ارائه شوند.
مزیت دیگر این است که شما میتوانید پیچیدگیها را در پشتصحنه پنهان کرده و یک رابط ساده به کاربران ارائه دهید. همچنین چون APIها کوچک و قابل کش شدن هستند، بسیار سریع خواهند بود.
حالا بیایید ببینیم API Proxy چیست و چرا باید از آن استفاده کنیم.
API Proxies 🛡
API Proxy یک کلاس است که بین کلاینت و API شما قرار میگیرد. در اصل، API Proxy نقش یک قرارداد (Contract) بین شما و توسعهدهندگانی که از API شما استفاده میکنند را دارد.
به جای اینکه دسترسی مستقیم به سرویسهای پشتیبان (Backend) API خود بدهید، که ممکن است با تغییرات و بازطراحیهای آینده دچار مشکل شوند، شما به مصرفکنندگان API اطمینان میدهید که قرارداد API همواره پایدار و معتبر باقی خواهد ماند، حتی زمانی که سرویسهای پشتیبان تغییر کنند یا گسترش یابند.
نمودار زیر ارتباط بین کلاینت، API Proxy، API اصلی و ارتباط API با منبع داده را نشان میدهد:
یک برنامه کنسولی برای پیادهسازی الگوی Proxy 🎯
در این بخش یک برنامه کنسولی طراحی میکنیم تا ببینید پیادهسازی الگوی Proxy چقدر ساده است. مثال ما شامل یک interface خواهد بود که توسط API و Proxy پیادهسازی میشود.
API پیام واقعی را برمیگرداند و Proxy پیام را از API دریافت کرده و به کلاینت منتقل میکند. البته Proxyها میتوانند بسیار فراتر از این عمل کنند؛ آنها میتوانند وظایفی مانند احراز هویت (Authentication)، مجوزدهی (Authorization)، مسیردهی بر اساس اعتبارنامهها (Routing) و بسیاری موارد دیگر را انجام دهند.
اما در این مثال، ما آن را تا حد ممکن ساده نگه میداریم تا سادگی الگوی Proxy را ببینید.
مراحل پیادهسازی: 💻
۱. یک برنامه کنسولی .NET Framework جدید ایجاد کنید.
۲. پوشههای Apis، Interfaces و Proxies را اضافه کنید.
۳. اینترفیس HelloWorldInterface را در پوشه Interfaces قرار دهید:
public interface HelloWorldInterface
{
string GetMessage();
}
متد GetMessage()
یک پیام را بهصورت رشته (string) برمیگرداند. کلاس API و کلاس Proxy هر دو این اینترفیس را پیادهسازی خواهند کرد.
ایجاد کلاس API
کلاس HelloWorldApi
اینترفیس HelloWorldInterface
را پیادهسازی میکند. آن را در پوشه Apis اضافه کنید:
internal class HelloWorldApi : HelloWorldInterface
{
public string GetMessage()
{
return "Hello World!";
}
}
همانطور که میبینید، کلاس API اینترفیس را پیادهسازی کرده و پیام "Hello World!" را بازمیگرداند. همچنین این کلاس بهصورت internal
تعریف شده است تا مستقیماً برای فراخوانیکنندگان خارجی در دسترس نباشد.
ایجاد کلاس Proxy
کلاس HelloWorldProxy
را به پوشه Proxies اضافه کنید:
public class HelloWorldProxy : HelloWorldInterface
{
public string GetMessage()
{
return new HelloWorldApi().GetMessage();
}
}
کلاس Proxy بهصورت public
تعریف شده است، چون توسط کلاینتها فراخوانی میشود. این کلاس متد GetMessage() را در کلاس API فراخوانی کرده و نتیجه را به فراخوانیکننده برمیگرداند.
تغییر متد Main()
static void Main(string[] args)
{
Console.WriteLine(new HelloWorldProxy().GetMessage());
Console.ReadKey();
}
کلاس Main متد GetMessage()
از کلاس Proxy را فراخوانی میکند. Proxy هم کلاس API را فراخوانی کرده و نتیجه در کنسول چاپ میشود. سپس کنسول منتظر فشار یک کلید میماند تا بسته شود.
کد را اجرا کنید و خروجی را ببینید؛ شما با موفقیت یک کلاس Proxy برای API پیادهسازی کردهاید.
نکته مهم
Proxyها میتوانند بسیار ساده یا پیچیده باشند. اما آنچه در اینجا انجام دادید پایه موفقیت در استفاده از الگوی Proxy است.
در این فصل قرار است یک API واقعی بسازیم. پس بیایید ببینیم قرار است چه چیزی ایجاد کنیم و سپس کار را شروع کنیم. پس از تکمیل پروژه، شما یک API کاربردی خواهید داشت که یک تقویم پرداخت سود ماهانه (Dividend Payment Calendar) را بهصورت JSON تولید میکند.
راهنمای طراحی API 📝
برای نوشتن یک API مؤثر، برخی دستورالعملهای پایه وجود دارند:
-
منابع (Resources) باید اسمهای جمع باشند.
-
مثال:
http://wholesale-website.com/api/customers/1 http://wholesale-website.com/api/products/20
-
این URLها مسیرهای api/controller/id را دنبال میکنند.
-
-
روابط در دامنه تجاری نیز باید در URLها منعکس شوند:
http://wholesale-website.com/api/categories/12/products
این فراخوانی لیست محصولات دسته ۱۲ را برمیگرداند.
-
استفاده از افعال در منابع:
-
هنگام ارسال درخواست HTTP:
GET
برای دریافت آیتمهاHEAD
برای فقط دریافت هدرهاPOST
برای افزودن یا ذخیره منبع جدیدPUT
برای جایگزینی منبعDELETE
برای حذف منبع
-
-
منابع را سبک نگه دارید و از Query Parameters استفاده کنید.
-
برای صفحهبندی (Pagination) نتایج، مجموعهای از لینکهای آماده در اختیار کلاینت قرار دهید.
-
RFC 5988 هدرهای لینک را معرفی کرده است:
<https://wholesale-website.com/api/products?page=10&per_page=100>; rel="next" <https://wholesale-website.com/api/products?page=11&per_page=100>; rel="last"
-
-
نسخهبندی API:
https://wholesale-website.com/api/v1/cart https://wholesale-website.com/api/v2/cart
این روش ساده است و یافتن نسخه درست API را آسان میکند.
-
JSON بهترین قالب نمایش دادههاست: خواناتر و کمحجمتر از XML.
- در متدهای
POST
،PUT
وPATCH
، هدرcontent-type
را رویapplication/json
قرار دهید، در غیر این صورت کد ۴۱۵ (Unsupported Media Type) برگردانید. - از Gzip بهصورت پیشفرض استفاده کنید تا مصرف پهنای باند کاهش یابد.
- در متدهای
-
همیشه از HTTPS (TLS) استفاده کنید.
- احراز هویت و مجوزدهی در هدر انجام شود.
- در مثالهای قبل، از هدر
x-api-key
استفاده کردیم. - دسترسیهای غیرمجاز باید کد ۴۰۳ (Forbidden) برگردانند.
-
کدهای وضعیت HTTP را بهدرستی استفاده کنید:
200
موفقیت404
یافت نشد- لیست کامل: https://httpstatuses.com
-
OAuth 2.0 استاندارد صنعت برای مجوزدهی است:
https://oauth.net/2/ -
API باید مستندات کامل و بهروز با مثالهای واضح داشته باشد.
- ظاهر مستندات باید جذاب و خوانا باشد.
- در این فصل با Swagger برای ایجاد مستندات آشنا میشویم.
-
مقیاسپذیری را از همان ابتدا در نظر بگیرید.
- در پروژه Dividend Calendar API در فصل بعد، خواهید دید چگونه محدودیت تعداد درخواستها (Throttling) را اعمال میکنیم.
-
امنیت و عملکرد:
- API Proxy میتواند کلاینت را از دسترسی مستقیم به API منع کند.
- Proxy میتواند در همان پروژه یا روی یک API خارجی باشد.
- Proxy از افشای ساختار دیتابیس جلوگیری میکند.
-
پاسخهای ارسالی به کلاینت هرگز نباید با ساختار دیتابیس مطابقت داشته باشند.
- این کار امنیت را کاهش میدهد و ممکن است به مهاجمان سرنخ دهد.
- شناسهها را پنهان کنید.
-
منابع (Resources) هر چیزی هستند که میتوان روی آنها عملی انجام داد:
- دادههای دانشآموزان، فایلهای ویدئویی یا صوتی، تصاویر، قالبهای گزارش و ...
-
منابع معمولاً بهصورت مجموعهای (Collection) هستند.
- مثال:
Students
مجموعهای از نوعStudent
.
- مثال:
-
URLs = Endpoints هستند.
- Endpoints آدرس منابع را مشخص میکنند.
- باید شامل اسمهای جمع باشند و از افعال پرهیز شود.
- برای مجموعههای بزرگ، صفحهبندی لازم است.
- در درخواستهای خیلی طولانی، پارامترها در بدنه POST قرار بگیرند.
-
افعال HTTP:
POST
→ اضافه کردنGET
→ دریافتPUT
→ جایگزینیPATCH
→ بروزرسانی جزئیDELETE
→ حذف
-
نامگذاری فیلدها، متدها و پراپرتیها:
- از هر قرارداد نامگذاری که دوست دارید استفاده کنید، اما سازگار و یکپارچه باشد.
- در JSON معمولاً از camelCase استفاده میشود.
- در C# از قراردادهای استاندارد C# پیروی کنید.
-
نسخهبندی (Versioning):
- بسیار مهم برای سازگاری با نسخههای قدیمی است.
- بهترین روش: قرار دادن نسخه در URL، مثل
v1
یاv2
.
-
کلیدهای API را محرمانه نگه دارید:
- از Azure Key Vault یا ابزارهای مشابه برای ذخیره امن استفاده کنید.
- برای امنیت بیشتر، خود API را نیز با کلید یا روشهای مناسب محافظت کنید.
مرزهای نرمافزاری مشخص (Well-defined software boundaries)
هیچکس در شرایط عادی کدهای درهموبرهم (spaghetti code) را دوست ندارد. این نوع کدها خواندن، نگهداری و گسترش آنها را بسیار سخت میکند. بنابراین هنگام طراحی یک API، میتوان با ایجاد مرزهای نرمافزاری مشخص از این مشکل جلوگیری کرد. در طراحی مبتنی بر دامنه (Domain-Driven Design یا DDD)، این مرزهای مشخص را کانتکست محدود (Bounded Context) مینامند.
از نظر تجاری، یک کانتکست محدود بهمعنای یک واحد عملیاتی کسبوکار است، مانند: منابع انسانی (HR)، مالی، خدمات مشتری، زیرساخت و غیره. این واحدهای عملیاتی کسبوکار دامنه (Domain) نامیده میشوند و هر دامنه میتواند به زیردامنههای کوچکتر تقسیم شود. این زیردامنهها نیز در صورت نیاز به بخشهای کوچکتر تقسیم میشوند.
با تقسیم یک کسبوکار به واحدهای عملیاتی، میتوان کارشناسان دامنه را در هر بخش بهکار گرفت. در ابتدای پروژه، یک زبان مشترک (Ubiquitous Language) تعریف میشود تا کسبوکار با اصطلاحات فنی آشنا شود و تیمهای IT هم اصطلاحات تجاری را بفهمند. وقتی زبان مشترک بین کسبوکار و تیم فنی وجود داشته باشد، احتمال بروز خطا بهدلیل سوءتفاهم کاهش پیدا میکند.
تقسیم یک پروژه بزرگ به زیردامنهها باعث میشود تیمهای کوچکتر بتوانند بهصورت مستقل روی بخشهای مختلف کار کنند. بنابراین تیمهای توسعه بزرگ میتوانند به گروههای کوچکتر تقسیم شوند و بهطور همزمان روی پروژههای گوناگون فعالیت کنند.
نکته: موضوع DDD بسیار گسترده است و در این بخش بهطور کامل پوشش داده نمیشود. برای اطلاعات بیشتر میتوانید به بخش مطالعه بیشتر (Further Reading) در انتهای این فصل مراجعه کنید.
پنهانسازی بخشهای غیرضروری
در APIها، تنها چیزهایی که باید در دسترس قرار بگیرند، اینترفیسها (Interfaces) و نقاط پایانی (Endpoints) هستند که نقش قرارداد را دارند. سایر بخشها باید از دید مشترک یا مصرفکننده مخفی بمانند.
حتی پایگاهدادههای بزرگ را میتوان بهگونهای تقسیم کرد که هر API پایگاهداده مختص به خود داشته باشد. با توجه به پیچیدگی بالای وبسایتها امروزه، میتوان مایکروسرویسها (Microservices)، مایکرودیتابیسها (Micro-databases) و مایکروفِرِنتاندها (Micro-frontends) داشت.
مایکروفِرنتاند چیست؟
مایکروفِرنتاند (Micro-frontend) بخشی کوچک از یک صفحه وب است که بهصورت پویا بر اساس تعاملات کاربر بارگذاری و تغییر داده میشود. این بخش با یک API ارتباط برقرار کرده و API نیز به یک مایکرودیتابیس متصل میشود.
این روش بهویژه برای برنامههای تکصفحهای (Single-Page Applications یا SPAs) بسیار ایدهآل است.
SPA چیست و چگونه کار میکند؟
SPAها وبسایتهایی هستند که تنها از یک صفحه تشکیل شدهاند. هر زمان که کاربر عملی انجام دهد، فقط بخش مورد نیاز از صفحه بهروزرسانی میشود و باقی صفحه بدون تغییر باقی میماند.
برای مثال فرض کنید صفحه وب شما یک بخش کناری (Aside) دارد که تبلیغات نمایش میدهد. این تبلیغات بهصورت قطعات HTML در پایگاهداده ذخیره شدهاند. این بخش کناری طوری تنظیم شده است که هر ۵ ثانیه یکبار بهروزرسانی شود. وقتی زمان بهروزرسانی برسد:
- Aside درخواستی به API میفرستد.
- API با الگوریتم مناسب، یک تبلیغ جدید از پایگاهداده انتخاب میکند.
- سند HTML بهروزرسانی شده و تبلیغ جدید نمایش داده میشود.
این روند همان چیزی است که چرخهی حیات SPA نام دارد.
این بخش کناری (Aside) خودش یک مرز نرمافزاری مشخص محسوب میشود. این بخش هیچ نیازی ندارد که چیزی درباره سایر قسمتهای صفحهای که در آن قرار دارد بداند. تنها وظیفهاش این است که هر ۵ ثانیه یک تبلیغ جدید نمایش دهد. ⏱️🖼️
نمایشگر قبلی یک SPA را نشان میدهد که از طریق یک API Proxy با یک RESTful API ارتباط برقرار میکند و آن API قادر است به اسناد و پایگاههای داده دسترسی داشته باشد.
[۲۶۰]
طراحی و توسعه APIها
فصل ۹
تنها اجزای تشکیلدهنده یک Aside عبارتاند از یک قطعه سند HTML، یک Microservice و یک Database. یک تیم کوچک میتواند روی این اجزا با هر فناوری که ترجیح میدهد و با آن راحتتر است کار کند. یک SPA کامل میتواند شامل صدها Micro-document، Micro-service و Micro-database باشد. نکته کلیدی اینجاست که این سرویسها میتوانند با هر فناوری ساخته شوند و هر تیمی بتواند بهطور مستقل روی آنها کار کند. همچنین چندین پروژه میتوانند بهصورت همزمان توسعه یابند.
در محدوده مرزی (Bounded Context) خود میتوانیم از روششناسیهای نرمافزاری زیر برای بهبود کیفیت کد استفاده کنیم:
- اصول SOLID (شامل Single Responsibility، Open/Closed، Liskov، Interface Segregation و Dependency Inversion)
- Don't Repeat Yourself (DRY)
- You Ain't Gonna Need It (YAGNI)
- Keep It Simple, Stupid (KISS)
این روشها با هم به خوبی کار میکنند تا کدهای تکراری را حذف کنند، از نوشتن کدهای غیرضروری جلوگیری کنند و اشیا و متدها را کوچک و ساده نگه دارند. دلیل اینکه ما کلاسها و متدها را توسعه میدهیم این است که هرکدام باید فقط یک کار انجام دهند و آن را بهخوبی انجام دهند.
Namespaces برای ایجاد گروهبندی منطقی استفاده میشوند. ما میتوانیم از Namespaces برای تعریف مرزهای نرمافزاری استفاده کنیم. هر چه Namespace خاصتر باشد، برای برنامهنویس معنادارتر خواهد بود. Namespaces معنادار به برنامهنویسان کمک میکنند تا کد را بخشبندی کنند و بهراحتی آنچه را که نیاز دارند پیدا کنند. از Namespaces برای گروهبندی منطقی Interfaceها، Classها، Structها و Enumها استفاده کنید.
در بخش بعدی 📚
یاد میگیرید که چگونه یک API را با استفاده از RAML طراحی کنید. سپس از RAML File یک C# API تولید خواهید کرد.
اهمیت مستندسازی با کیفیت بالا برای APIها 📝
هنگام کار روی یک پروژه، لازم است تمام APIهایی که در حال حاضر استفاده میشوند را بشناسید. دلیل این موضوع این است که ممکن است بهطور ناخواسته کدی بنویسید که قبلاً وجود داشته است، و این منجر به هدر رفت تلاش میشود. علاوه بر این، اگر نسخه جدیدی از کدی که از قبل وجود دارد بنویسید، اکنون دو نسخه از کد دارید که یک کار انجام میدهند. این باعث پیچیدگی نرمافزار میشود و هزینه نگهداری را افزایش میدهد، زیرا هر دو نسخه باید پشتیبانی شوند. همچنین احتمال بروز Bug نیز افزایش پیدا میکند.
در پروژههای بزرگ که در چندین فناوری و مخزن (Repository) پخش شدهاند و تیمهایی با نرخ جابجایی بالای نیروها دارند، بهویژه جایی که مستندات وجود ندارد، تکرار کد به یک مشکل واقعی تبدیل میشود. گاهی اوقات تنها یک یا دو متخصص حوزه (Domain Expert) وجود دارند و اکثر اعضای تیم اصلاً سیستم را نمیشناسند. من روی پروژههایی از این نوع کار کردهام و نگهداری و توسعه آنها واقعاً آزاردهنده است.
به همین دلیل است که مستندسازی API برای هر پروژه، صرفنظر از اندازه آن، حیاتی است. در دنیای توسعه نرمافزار، رفتوآمد نیروها امری اجتنابناپذیر است، بهویژه زمانی که فرصتهای کاری بهتری ارائه شود. اگر فردی که پروژه را ترک میکند متخصص اصلی حوزه باشد، دانش خود را با خود میبرد. اگر مستندات وجود نداشته باشد، توسعهدهندگان جدید با یک منحنی یادگیری تند روبهرو خواهند شد و مجبور میشوند با خواندن کدها پروژه را بفهمند. اگر کدها نامرتب و پیچیده باشند، این روند میتواند برای نیروی تازهوارد بسیار دردسرساز باشد.
در نتیجه، به دلیل کمبود دانش سیستم، برنامهنویسان تمایل پیدا میکنند که تقریباً همهچیز را از صفر بنویسند تا کار را بهموقع تحویل دهند. این موضوع معمولاً باعث تکرار کد میشود و استفاده مجدد از کدها اتفاق نمیافتد. نتیجه این است که نرمافزار پیچیده، خطاپذیر و سخت برای توسعه و نگهداری میشود.
اکنون شما میدانید که چرا APIها باید مستندسازی شوند. یک API با مستندسازی کامل باعث درک بهتر برنامهنویسان میشود و تمایل به استفاده مجدد از آن را افزایش میدهد، در نتیجه احتمال تکرار کد کاهش یافته و از تولید نرمافزار پیچیده و سختنگهداری جلوگیری میشود.
همچنین باید مراقب هر کدی باشید که بهعنوان Deprecated یا Obsolete علامتگذاری شده باشد. کدهای Deprecated در نسخههای بعدی حذف میشوند و کدهای Obsolete دیگر استفاده نمیشوند. اگر شما از APIهایی استفاده میکنید که Deprecated یا Obsolete هستند، باید در اولویت قرار دهید تا آنها را رفع یا جایگزین کنید.
حالا که اهمیت مستندسازی با کیفیت API را فهمیدید، به سراغ ابزاری به نام Swagger میرویم. Swagger یک ابزار ساده و کارآمد برای تولید مستندسازی زیبا و با کیفیت برای APIها است.
توسعه API با Swagger 🚀
Swagger یک مجموعه قدرتمند از ابزارها فراهم میکند که بر توسعه API متمرکز هستند. با Swagger میتوانید کارهای زیر را انجام دهید:
- طراحی (Design): طراحی API و مدلسازی آن براساس استانداردهای مشخصات.
- ساخت (Build): ساخت یک API پایدار و قابل استفاده مجدد در C#.
- مستندسازی (Document): ارائه مستنداتی تعاملی برای توسعهدهندگان.
- آزمایش (Test): آزمایش ساده API.
- استانداردسازی (Standardize): اعمال محدودیتها و راهنماهای سازمانی بر معماری API.
ما قصد داریم Swagger را در پروژه ASP.NET Core 3.0+ خود راهاندازی کنیم. بنابراین، ابتدا پروژه را در Visual Studio 2019 ایجاد کنید. Web API را انتخاب کنید و تنظیمات No Authentication را فعال کنید.
پیش از ادامه، توجه داشته باشید که Swagger بهصورت خودکار مستندات زیبا و کاربردی تولید میکند و برای راهاندازی آن به کد بسیار کمی نیاز است؛ به همین دلیل بسیاری از APIهای مدرن از Swagger استفاده میکنند.
پیش از اینکه بتوانیم از Swagger استفاده کنیم، ابتدا باید پشتیبانی آن را در پروژه نصب کنیم. برای نصب Swagger باید نسخه ۵ یا بالاتر از بسته Swashbuckle.AspNetCore را نصب کنید. در زمان نگارش این متن، نسخه موجود در NuGet، ۵.۳.۳ است. پس از نصب، باید خدمات Swagger را به مجموعه سرویسهای خود اضافه کنیم. در اینجا، ما فقط از Swagger برای مستندسازی API خود استفاده میکنیم. در کلاس Startup.cs، خط زیر را به متد ConfigureServices() اضافه کنید:
services.AddSwaggerGen(swagger =>
{
swagger.SwaggerDoc("v1", new OpenApiInfo { Title = "Weather Forecast API" });
});
در کدی که اضافه کردیم، سرویس مستندسازی Swagger به مجموعه سرویسها اضافه شده است. نسخه API ما v1 و عنوان آن Weather Forecast API است.
اکنون باید متد Configure() را بهروزرسانی کنیم تا Middleware مربوط به Swagger را اضافه کنیم، به این صورت، بلافاصله پس از if statement:
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Weather Forecast API");
});
در متد Configure() به برنامه خود میگوییم که از Swagger و Swagger UI استفاده کند و مسیر Swagger Endpoint برای Weather Forecast API را مشخص میکنیم.
سپس باید بسته Swashbuckle.AspNetCore.Newtonsoft را از NuGet نصب کنید (نسخه ۵.۳.۳ در زمان نگارش). بعد از آن، خط زیر را به متد ConfigureServices() خود اضافه کنید:
services.AddSwaggerGenNewtonsoftSupport();
ما پشتیبانی از Newtonsoft را برای تولید مستندات Swagger اضافه کردیم. همین و بس! 🎯
با این کار، Swagger آمادهی اجرا است. حالا پروژهی خود را اجرا کنید و به این آدرس بروید:
https://localhost:PORT_NUMBER/swagger/index.html
باید صفحهی وب زیر را ببینید: 🌐
حالا بیایید بررسی کنیم که چرا باید immutable structها را به جای mutable objectها پاس بدهیم.
عبور دادن immutable structها به جای mutable objectها 🧩
در این بخش، یک برنامهی کامپیوتری خواهیم نوشت که ۱ میلیون شیء و ۱ میلیون immutable struct را پردازش میکند. خواهید دید که structها از نظر عملکرد (performance) بسیار سریعتر از objectها هستند.
ما کدی مینویسیم که ۱ میلیون object را در ۱۴۴۰ میلیثانیه پردازش میکند و ۱ میلیون struct را در ۸۴۱ میلیثانیه. این یعنی ۵۹۹ میلیثانیه اختلاف. شاید این زمان خیلی به نظر نرسد، اما زمانی که با مجموعهدادههای عظیم کار میکنید، استفاده از immutable structها نسبت به mutable objectها بهبودهای بزرگی در عملکرد ایجاد میکند. 🚀
مشکل mutable objectها در برنامههای چندنخی (Multi-threading)
مقادیر در mutable objectها میتوانند بین رشتهها (threads) تغییر کنند که میتواند فاجعهبار باشد. تصور کنید در حساب بانکیتان ۱۵٬۰۰۰ پوند دارید و به صاحبخانهتان ۴۳۵ پوند اجاره پرداخت میکنید. حساب شما دارای سقف برداشت (overdraft limit) است. حالا همزمان، شخص دیگری در حال پرداخت ۲۳٬۰۰۰ پوند به یک شرکت خودرو برای خرید ماشین است. مقدار حساب شما توسط thread خریدار ماشین تغییر میکند و شما به جای ۴۳۵ پوند، ۲۳٬۰۰۰ پوند به صاحبخانه پرداخت میکنید و حساب شما ۸٬۰۰۰ پوند بدهکار میشود!
نیازی نیست کدی برای مثال تغییر دادهی mutable بین threads بنویسیم، زیرا این موضوع در فصل ۸ – Threading and Concurrency پوشش داده شده است.
نکات کلیدی این بخش 📝
- Structها سریعتر از objectها هستند.
- Immutable structها thread-safe هستند.
هنگام ایجاد و پاس دادن دادهها، structها نسبت به objectها عملکرد بهتری دارند. همچنین میتوان structها را immutable کرد تا thread-safe باشند. در اینجا یک برنامهی کوچک خواهیم نوشت.
ایجاد پروژه
یک برنامهی کنسول جدید در .NET Framework به نام CH11_WellDefinedBoundaries اضافه کنید و کلاس زیر را ایجاد نمایید:
public class PersonObject
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
این کلاس برای ایجاد ۱ میلیون شیء person استفاده خواهد شد.
حالا struct زیر را اضافه کنید:
public struct PersonStruct
{
private readonly string _firstName;
private readonly string _lastName;
public PersonStruct(string firstName, string lastName)
{
_firstName = firstName;
_lastName = lastName;
}
public string FirstName => _firstName;
public string LastName => _lastName;
}
این struct غیرقابلتغییر (immutable) است. مقادیر فقط از طریق constructor مقداردهی میشوند و سپس برای ایجاد ۱ میلیون struct استفاده خواهند شد.
مقایسهی عملکرد با متدهای CreateObjects و CreateStructs
حالا برنامه را برای نمایش تفاوت عملکرد بین object و struct تغییر میدهیم.
متد CreateObjects را اضافه کنید:
private static void CreateObjects()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var people = new List<PersonObject>();
for (var i = 1; i <= 1000000; i++)
{
people.Add(new PersonObject
{
FirstName = "Person",
LastName = $"Number {i}"
});
}
stopwatch.Stop();
Console.WriteLine($"Object: {stopwatch.ElapsedMilliseconds}, Object Count: {people.Count}");
GC.Collect();
}
همانطور که میبینید، stopwatch را شروع میکنیم، یک لیست جدید ایجاد میکنیم و ۱ میلیون شیء person به آن اضافه میکنیم. سپس stopwatch را متوقف کرده، نتیجه را در پنجره چاپ میکنیم و garbage collector را برای پاکسازی منابع فراخوانی میکنیم.
متد CreateStructs را اضافه کنید:
private static void CreateStructs()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var people = new List<PersonStruct>();
for (var i = 1; i <= 1000000; i++)
{
people.Add(new PersonStruct("Person", $"Number {i}"));
}
stopwatch.Stop();
Console.WriteLine($"Struct: {stopwatch.ElapsedMilliseconds}, Struct Count: {people.Count}");
GC.Collect();
}
این متد همان کار را انجام میدهد، اما بهجای objectها، structها را ایجاد و به لیست اضافه میکند.
متد Main
در نهایت متد Main را به شکل زیر تغییر دهید:
static void Main(string[] args)
{
CreateObjects();
CreateStructs();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
در این متد، هر دو متد را فراخوانی میکنیم و سپس منتظر میمانیم تا کاربر کلیدی فشار دهد و برنامه خاتمه یابد.
پروژه را اجرا کنید و باید خروجی زیر را ببینید: 👇
همانطور که در اسکرینشات قبلی دیدید، ایجاد یک میلیون شیء و افزودن آنها به یک لیست از اشیاء ۱,۴۴۰ میلیثانیه زمان برد، در حالی که ایجاد یک میلیون ساختار (Struct) و افزودن آنها به یک لیست از ساختارها تنها ۸۴۱ میلیثانیه طول کشید.
بنابراین، نهتنها میتوانید Structها را Immutable و Thread-safe کنید (چون بین رشتهها قابل تغییر نیستند)، بلکه از نظر عملکردی بسیار سریعتر از اشیاء هستند. پس اگر با حجم زیادی از داده سروکار دارید، استفاده از Structها میتواند مقدار زیادی از زمان پردازش شما را ذخیره کند.
علاوه بر این، اگر از سرویسهای ابری استفاده میکنید که به ازای هر چرخهی زمانی پردازش هزینه دریافت میکنند، استفاده از Structها به جای اشیاء میتواند منجر به صرفهجویی مالی شود. 💰
حالا بیایید نگاهی به نوشتن تست برای APIهای شخص ثالث (Third-Party APIs) بیندازیم که قرار است از آنها استفاده کنید.
شاید بپرسید: «چرا باید APIهای شخص ثالث را تست کنیم؟» سؤال خوبی است. دلیلش این است که همانند کدهای خودتان، کدهای شخص ثالث هم ممکن است دچار خطاهای برنامهنویسی باشند.
به عنوان مثال، من زمانی در حال توسعهی یک وبسایت پردازش اسناد برای یک شرکت حقوقی بودم و با مشکل جدی مواجه شدم. بعد از بررسی زیاد، متوجه شدم مشکل از جاوااسکریپت معیوبی بود که در API مایکروسافت تعبیه شده بود.
تصویر زیر صفحه Issues در GitHub برای Microsoft Cognitive Toolkit را نشان میدهد که ۷۳۸ مشکل باز (Outstanding Issues) دارد:
همانطور که از Microsoft Cognitive Toolkit مشاهده میکنید، APIهای شخص ثالث هم مشکلاتی دارند. ⚠️
این یعنی مسئولیت اطمینان از عملکرد صحیح APIهای شخص ثالثی که استفاده میکنید، بر عهدهی شماست. اگر با باگ یا خطایی مواجه شدید، عمل درست این است که به شخص ثالث اطلاع دهید. اگر API Open Source باشد و شما به کد منبع دسترسی داشته باشید، حتی میتوانید کد را بررسی کرده و اصلاحات خود را ارائه دهید.
هرگاه با باگهایی در کد شخص ثالث مواجه شدید که در زمان لازم برای رسیدن به مهلت تحویل پروژه رفع نمیشوند، یکی از گزینهها این است که یک Wrapper Class بنویسید که تمامی Constructorها، Methodها و Propertyها را داشته باشد و آنها را به همان Constructorها، Methodها و Propertyهای کلاس شخص ثالث ارجاع دهد؛ با این تفاوت که شما نسخهی بدون باگ خود را برای Property یا Method دارای باگ مینویسید. 📦
فصل ۱۱، Addressing Cross-Cutting Concerns، بخشهایی دربارهی Proxy Pattern و Decorator Pattern دارد که در نوشتن Wrapper Class به شما کمک میکند.
تست کردن APIهای خودتان 🧪
در فصل ۶ (Unit Testing) و فصل ۷ (End-to-End System Testing) با مثالهای کدنویسی، نحوهی تست کد خودتان را مشاهده کردید.
شما همیشه باید APIهای خودتان را تست کنید تا از کیفیت آنها اطمینان کامل داشته باشید. بنابراین، به عنوان یک برنامهنویس، قبل از اینکه کد را به تیم کیفیت بسپارید، باید Unit Test انجام دهید. سپس تیم Quality Assurance باید Integration و Regression Testing انجام دهد تا مطمئن شود API سطح کیفیت توافقشده شرکت را رعایت میکند. ✅
API شما ممکن است دقیقا همان چیزی را انجام دهد که کسبوکار خواسته و بدون باگ باشد؛ اما وقتی با سیستم یکپارچه میشود، آیا در شرایط خاص رفتارهای غیرمنتظره رخ میدهد که شما نتوانستهاید تست کنید؟
بارها با شرایطی مواجه شدهام که کد روی کامپیوتر یک نفر درست کار میکند اما روی دیگران نه. اغلب دلیل منطقی برای این اتفاق وجود ندارد و پیدا کردن آن میتواند بسیار خستهکننده و وقتگیر باشد. اما شما میخواهید این مشکلات قبل از تحویل کد به QA و قطعاً قبل از انتشار در محیط Production حل شده باشند. 🛠️
تست برنامهها باید شامل موارد زیر باشد
- وقتی مقدار ورودی صحیح داده میشود، متد تحت تست نتیجهی درست را خروجی دهد.
- وقتی مقدار ورودی نادرست داده میشود، متد پاسخ مناسب را بدهد و Crash نکند. ⚡
به یاد داشته باشید که API شما باید فقط آنچه کسبوکار خواسته را شامل شود و جزئیات داخلی را در اختیار کلاینت قرار ندهد.
در اینجا است که Product Backlog، که بخشی از Scrum Project Management است، مفید واقع میشود.
Product Backlog فهرستی از ویژگیهای جدید و Technical Debt است که شما و تیمتان روی آنها کار خواهید کرد. هر آیتم در Product Backlog دارای توضیح و Acceptance Criteria است، همانطور که در اسکرینشات زیر نشان داده شده: 📋
شما Unit Testها را بر اساس Acceptance Criteria مینویسید. ✅
تستهای شما شامل مسیرهای عادی اجرای کد و مسیرهای غیرعادی اجرای کد خواهند بود.
با استفاده از این اسکرینشات به عنوان مثال، دو Acceptance Criteria داریم:
- دادهها بهطور موفقیتآمیز از APIهای شخص ثالث دریافت میشوند.
- دادهها بهطور موفقیتآمیز در Cosmos DB ذخیره میشوند.
در این دو معیار، میدانیم که ما APIهایی برای دریافت دادهها فراخوانی خواهیم کرد. این دادهها از سرویسهای شخص ثالث به دست میآیند و سپس در پایگاه داده ذخیره میشوند. در نگاه اول، این مشخصات کمی مبهم به نظر میرسند. در دنیای واقعی، اغلب اینگونه است.
با توجه به ابهام مشخصات، ما فرض میکنیم که این مشخصات عمومی و قابل اعمال برای APIهای مختلف هستند و دادههای بازگشتی JSON خواهند بود. همچنین فرض میکنیم که دادههای JSON بازگشتی به صورت خام در پایگاه داده Cosmos DB ذخیره میشوند. 🌐
چه تستهایی میتوانیم برای اولین Acceptance Criteria بنویسیم؟
- وقتی URL با پارامترها داده میشود، اطمینان حاصل کنید که وضعیت ۲۰۰ و JSON بازگشتی برای درخواست GET وقتی تمام اطلاعات صحیح ارائه شده است، دریافت میشود.
- اطمینان حاصل کنید که وضعیت ۴۰۱ دریافت میشود وقتی GET Request غیرمجاز انجام شده است.
- اطمینان حاصل کنید که وضعیت ۴۰۳ دریافت میشود وقتی کاربر احراز هویت شده اجازه دسترسی ندارد.
- اطمینان حاصل کنید که وضعیت ۵۰۰ دریافت میشود وقتی سرور از کار افتاده است.
چه تستهایی میتوانیم برای دومین Acceptance Criteria بنویسیم؟
- اطمینان حاصل کنید که دسترسی غیرمجاز به پایگاه داده رد شود.
- اطمینان حاصل کنید که API در مواقعی که پایگاه داده در دسترس نیست، با آرامش عمل میکند.
- اطمینان حاصل کنید که دسترسی مجاز به پایگاه داده داده میشود.
- اطمینان حاصل کنید که JSON در پایگاه داده با موفقیت درج میشود.
بنابراین، حتی از چنین مشخصات مبهمی، ما توانستیم هشت Test Case بدست آوریم.
تمام این موارد مسیر رفت و برگشت موفق به سرور شخص ثالث و سپس به پایگاه داده را تست میکنند و همچنین نقاط مختلفی که فرایند میتواند شکست بخورد را بررسی میکنند. اگر تمام این تستها موفق باشند، ما اعتماد کامل به کد خود خواهیم داشت و اطمینان داریم که کد از کنترل کیفیت عبور خواهد کرد. ✅
طراحی API با استفاده از RAML 🛠️
در این بخش، طراحی API با RAML را بررسی خواهیم کرد.
میتوانید اطلاعات کامل دربارهی تمام جنبههای RAML را از وبسایت رسمی RAML به دست آورید:
https://raml.org/developers/design-your-api
ما اصول اولیه RAML را با طراحی یک API بسیار ساده با استفاده از API Workbench در Atom یاد خواهیم گرفت.
شروع میکنیم با نصب ابزارها.
نصب Atom و API Workbench توسط MuleSoft
مراحل به این صورت است:
- ابتدا Atom را از http://atom.io نصب کنید.
- سپس روی Install a Package کلیک کنید. ⚙️
۳. سپس در کادر جستجو عبارت api-workbench by MuleSoft را جستجو کرده و آن را نصب کنید. ✅
پس از این مرحله، Atom و API Workbench شما آماده خواهند بود تا شروع به طراحی و تولید API با استفاده از RAML کنید. 🛠️
۴. اکنون که پکیجها نصب شدند، بیایید به مرحلهی ایجاد پروژه برویم. 🖥️
ایجاد پروژه
مراحل به این صورت است:
- از منوی File گزینه Add Project Folder را انتخاب کنید.
- یک پوشهی جدید ایجاد کنید یا یک پوشهی موجود را انتخاب کنید. من یک پوشهی جدید با مسیر C:\Development\RAML ایجاد کرده و آن را باز میکنم.
- یک فایل جدید به پوشهی پروژه اضافه کنید و آن را Shop.raml نامگذاری کنید.
- روی فایل راستکلیک کرده و Add New | Create New API را انتخاب کنید.
- به آن هر نامی که میخواهید بدهید و سپس روی Ok کلیک کنید. حالا شما اولین طراحی API خود را ایجاد کردهاید. ✅
اگر به فایل RAML نگاه کنید، میبینید که محتویات آن قابل خواندن برای انسان هستند. API که ایجاد کردهایم، شامل یک دستور ساده GET است که یک رشته شامل کلمات "Hello World" را بازمیگرداند:
#%RAML 1.0
title: Pet Shop
types:
TestType:
type: object
properties:
id: number
optional?: string
expanded:
type: object
properties:
count: number
/helloWorld:
get:
responses:
200:
body:
application/json:
example: |
{
"message" : "Hello World"
}
این کد RAML است. مشاهده میکنید که بسیار شبیه JSON است، با کدی ساده، قابل خواندن و با تورفتگی مناسب.
فایل را حذف کنید. سپس از منوی Packages گزینه API Workbench | Create RAML Project را انتخاب کنید.
در پنجرهی Create RAML Project اطلاعات لازم را پر کنید، همانطور که در اسکرینشات بعدی نشان داده شده است.
تنظیمات در این پنجره باعث تولید کد RAML زیر میشوند:
#%RAML 1.0
title: Pet Shop
version: v1
baseUri: /petshop
types:
TestType:
type: object
properties:
id: number
optional?: string
expanded:
type: object
properties:
count: number
/helloWorld:
get:
responses:
200:
body:
application/json:
example: |
{
"message" : "Hello World"
}
🔹 تفاوت اصلی بین فایل RAML قبلی و این فایل، اضافه شدن خصوصیات version
و baseUri
است.
این تنظیمات همچنین محتویات پوشهی پروژه شما را بهروزرسانی میکنند، همانطور که در ادامه مشاهده خواهید کرد.
برای یک آموزش بسیار جامع در این موضوع، به آدرس زیر مراجعه کنید:
http://apiworkbench.com/docs/
این وبسایت همچنین جزئیات زیادی دربارهی اضافه کردن منابع و متدها، پر کردن بدنهی متدها و پاسخها، اضافه کردن زیرمنابع، افزودن مثالها و نوعها، ایجاد و استخراج نوعهای منابع، اضافه کردن پارامترهای نوع منبع و متد، استفادهی مجدد از Traits، نوعهای منابع و کتابخانهها، اضافه کردن انواع و منابع بیشتر، استخراج کتابخانهها و موارد بسیار دیگر که در این فصل نمیتوانیم پوشش دهیم، ارائه میدهد.
حالا که یک طراحی بیطرف از نظر زبان پیادهسازی داریم، چگونه میتوانیم API خود را در C# تولید کنیم؟
تولید API در C# از طراحی RAML بیطرف
برای این کار حداقل باید Visual Studio 2019 Community edition را نصب داشته باشید.
سپس مطمئن شوید که Visual Studio بسته است و ابزار MuleSoftInc.RAMLToolsforNET را دانلود و نصب کنید.
با نصب این ابزارها، مراحل لازم برای تولید چارچوب اسکلت (skeleton framework) API که قبلاً مشخص کردیم، به شرح زیر انجام میشود:
1️⃣ در Visual Studio 2019، یک پروژهی جدید از نوع .NET Framework console application ایجاد کنید.
2️⃣ روی پروژه راستکلیک کرده و گزینه Add RAML/OAS Contract را انتخاب کنید. این کار پنجرهی زیر را باز میکند:
3️⃣ روی Upload کلیک کنید و سپس فایل RAML خود را انتخاب کنید.
پنجرهی Import RAML/OAS نمایش داده خواهد شد.
این پنجره را مطابق تصویر پر کنید و سپس روی Import کلیک کنید:
پروژهی شما اکنون با وابستگیهای لازم بهروز خواهد شد و پوشهها و فایلهای جدیدی به برنامهی کنسول شما اضافه خواهند شد. شما سه پوشهی اصلی خواهید دید: Contracts، Controllers و Models.
در پوشهی Contracts، فایل RAML و اینترفیس IV1HelloWorldController قرار دارد. این اینترفیس شامل یک متد است:
Task<IHttpActionResult> Get()
کلاس v1HelloWorldController این اینترفیس را پیادهسازی میکند. حالا بیایید نگاهی به متد Get() پیادهسازیشده در کلاس کنترلر بیندازیم:
/// <summary>
/// /helloWorld
/// </summary>
/// <returns>HelloWorldGet200</returns>
public async Task<IHttpActionResult> Get()
{
// TODO: implement Get - route: helloWorld/helloWorld
// var result = new HelloWorldGet200();
// return Ok(result);
return Ok();
}
در کد بالا، نمونهسازی کلاس HelloWorldGet200 و مقدار برگشتی کامنت شده است. کلاس HelloWorldGet200 مدل ما است. میتوانیم مدل خود را با هر دادهای که میخواهیم بهروزرسانی کنیم. در مثال سادهی ما، زیاد وارد جزئیات نمیشویم و فقط رشتهی "Hello World!"
را بازمیگردانیم. خط بازشده را به شکل زیر تغییر دهید:
return Ok("Hello World!");
متد Ok() نوع OkNegotiatedContentResult
static void Main(string[] args)
{
Task.Run(async () =>
{
var hwc = new v1HelloWorldController();
var response = await hwc.Get() as OkNegotiatedContentResult<string>;
if (response is OkNegotiatedContentResult<string>)
{
var msg = response.Content;
Console.WriteLine($"Message: {msg}");
}
}).GetAwaiter().GetResult();
Console.ReadKey();
}
از آنجا که کد غیرهمزمان را در یک متد استاتیک اجرا میکنیم، باید کار را به صف thread pool اضافه کنیم. سپس کد را اجرا میکنیم و منتظر نتیجه میمانیم. وقتی کد بازگشت، فقط منتظر فشار یک کلید میمانیم و سپس برنامه خارج میشود. ⌨️🖥️
ما یک MVC API درون برنامهی کنسول ایجاد کردهایم و فراخوانیهای API را بر اساس فایل RAML که وارد کردیم اجرا کردهایم. این فرآیند برای وبسایتهای ASP.NET و ASP.NET Core نیز به همان شکل کار میکند.
حالا میخواهیم RAML را از یک API موجود استخراج کنیم. پروژهی dividend calendar API که قبلاً در این فصل ایجاد کردیم را بارگذاری کنید. سپس روی پروژه راستکلیک کرده و گزینهی Extract RAML را انتخاب کنید. پس از پایان استخراج، پروژه را اجرا کنید و URL را به شکل زیر تغییر دهید:
https://localhost:44325/raml
هنگامی که RAML استخراج میشود، فرآیند تولید کد یک کلاس RamlController به پروژه اضافه میکند و یک RAML view ایجاد میکند. اکنون API شما مستندسازی شده است و میتوانید آن را در RAML view مشاهده کنید. 📄🔗
با استفاده از RAML، میتوانید یک API طراحی کنید و سپس ساختار آن را تولید کنید و حتی یک API را به صورت معکوس تحلیل کنید. مشخصات RAML به شما کمک میکند تا API خود را طراحی کرده و تغییرات لازم را با اصلاح کد RAML اعمال کنید. اگر میخواهید اطلاعات بیشتری کسب کنید، میتوانید به وبسایت RAML مراجعه کنید تا بیشترین بهره را از مشخصات RAML ببرید.
حال، نگاهی به Swagger و نحوهی استفاده از آن در پروژههای ASP.NET Core 3+ خواهیم داشت. 🚀
جمعبندی 📝
در این فصل، دربارهی API صحبت کردیم و سپس بررسی کردیم که چگونه میتوان از API proxy به عنوان قرارداد بین خودمان و مصرفکنندگان API استفاده کرد. این کار باعث میشود API ما از دسترسی مستقیم افراد ثالث محافظت شود.
سپس به چند راهنمای طراحی برای بهبود کیفیت API پرداختیم. بعد، با Swagger آشنا شدیم و دیدیم چگونه میتوان Weather API را مستندسازی کرد. همچنین به موضوع تست APIها پرداختیم و توضیح دادیم چرا تست کد خودمان و کدهای ثالث که در پروژه استفاده میکنیم اهمیت دارد.
در نهایت، طراحی یک API مستقل از زبان برنامهنویسی با استفاده از RAML را دیدیم و آن را به پروژهای عملی در C# تبدیل کردیم.
در فصل بعد، پروژهای خواهیم نوشت تا نحوهی امنسازی کلیدها با Azure Key Vault و همچنین محافظت از API خود با API Key را نشان دهیم. اما قبل از آن، بیایید مغزتان را به کار بیندازیم تا ببینیم چه چیزهایی یاد گرفتهاید. 🧠
سوالات ❓
- API مخفف چیست؟
- REST مخفف چیست؟
- شش محدودیت REST چیست؟
- HATEOAS مخفف چیست؟
- RAML چیست؟
- Swagger چیست؟
- منظور از well-defined software boundary چیست؟
- چرا باید APIهایی که استفاده میکنید را بفهمید؟
- چه چیزی بهتر عمل میکند—struct یا object؟
- چرا باید APIهای ثالث را تست کنید؟
- چرا باید APIهای خود را تست کنید؟
- چگونه میتوانید تعیین کنید چه تستهایی برای کدتان بنویسید؟
- سه روش برای سازماندهی کد در well-defined software boundaries را نام ببرید.
منابع برای مطالعهی بیشتر 📚
- NUnit برای تست Web API در ASP.NET
- طراحی API با RAML
- استفاده از RAML در Atom
- مقدمهای بر استفاده از Swagger در ASP.NET Core
- صفحهی About در Swagger
- لیست کدهای وضعیت HTTP
- مشخصات لینکدهی وب RFC 5988
- صفحهی اصلی OAuth 2.0
- Wikipedia: Domain-driven design
- Hands-On Domain-Driven Design with .NET Core
- Test-Driven Development با C# و .NET Core و MVC
✅ این فصل به پایان رسید و شما اکنون با مفاهیم API، Swagger، RAML، تست API و ایجاد API مستقل از زبان آشنا شدهاید.