<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های مرتضی دلیل</title>
        <link>https://virgool.io/feed/@mortezadalil</link>
        <description>برنامه نویس و علاقمند به برنامه نویسی، سینما، فلسفه و هر چیزی که هیجان انگیز باشد. در ویرگول از روزمرگیهای مرتبط با علاقمندیهام خواهم نوشت. در توئیتر و جاهای دیگر @mortezadalil هستم.</description>
        <language>fa</language>
        <pubDate>2026-04-15 02:55:26</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/31628/avatar/UY6UgX.png?height=120&amp;width=120</url>
            <title>مرتضی دلیل</title>
            <link>https://virgool.io/@mortezadalil</link>
        </image>

                    <item>
                <title>صد اصطلاح انگلیسی برای شروع برنامه نویسی</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D8%A7%D8%B5%D8%B7%D9%84%D8%A7%D8%AD%D8%A7%D8%AA-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-apd3jabsxbya</link>
                <description>اگر برنامه نویس نیستید یا در شروع راه هستید این ویدیو به درد شما میخوره.دنیای برنامه نویسی خیلی بزرگ و تقریبا بی در و پیکره. اگر یک فرد ناآشنا با برنامه نویسی یک روز در یک تیم برنامه نویسی باشه شاید اگر ذره ای علاقه هم داشته باشه با شنیدن انبوهی از اصطلاحات نا آشنا، کاملا دلسرد میشه.به نظرم آشنا شدن با اصطلاحات به شکل سطحی، پیش از شروع دوره های برنامه نویسی تاثیر زیادی در درک مفاهیم در آینده داره.توی این ویدیو صد اصطلاح مهم و فاندامنتال برای یک برنامه نویس معرفی و با مثال توضیح داده شده.در آینده ای نزدیک قصد دارم همین ویدیو رو با توضیحات مفصل به فارسی تولید کنم. https://youtu.be/40nCIxzIDMw </description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Sat, 19 Aug 2023 03:12:39 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش برنامه نویسی از صفرِ صفرِ صفر</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-%D8%A7%D8%B2-%D8%B5%D9%81%D8%B1%D9%90-%D8%B5%D9%81%D8%B1%D9%90-%D8%B5%D9%81%D8%B1-vkuka7vhifzq</link>
                <description>شوق و اشتیاق برای برنامه نویس شدن هر روز بیشتر از دیروز میشه. پدیده هایی مثل ChatGPT یا اپلیکیشن های جذاب همه رو ترغیب کرده که وارد این دنیا بشن و این برای ماهایی که برنامه نویس بودیم یک فرصت هست. فرصتی که بیشتر بخونیم و بدونیم و عقب نیفتیم.چند ماه پیش دوره آنلاین آموزش برنامه نویسی رو برگزار کردم. تصمیم گرفتم این دوره رو ادیت کنم و توی یوتوب بذارم که همه بتونن استفاده کنند.اگر در اطراف خودتون آدم علاقمند به برنامه نویس و پر شور و فعال میشناسید این دوره رو بهش معرفی کنید. حدود 7 ساعت بدون توقف در مورد برنامه نویسی صحبت کردم و با کلی اسلاید و ایده هر چیزی که در توانم بود به زبان آوردم.(دوره 15 ساعت بوده. توی این ویدیو هم سرعتش زیاد شده هم فواصل بین صحبت ها کات شده که وقت شما عزیزان گرفته نشه)اگر قرار باشه برای یک نفر که هیچی از برنامه نویسی نمیدونه کلاس برگزار کنم این ویدیو دقیقا چیزی هست که توش میگم.از جاوااسکریپت شروع کردم و در ویدیو توضیح دادم که چرا Javascript گزینه مناسبی برای شروع برنامه نویسی هست. از وب شروع کردم از تعریف مرورگر و اینکه دقیقا مرورگر داره چه کاری انجام میده.(شاید بگید چرا ولی تمام تصمیم هام دلیل داره و برای همه چیز قبل از برگزاری دوره سناریو نوشتم)سالها به این موضوع فکر کردم که چطور میتونم یک نفر رو توی پروسه یادگیری برنامه نویسی قرار بدم، از کجا باید شروع کنم، چه چیزهایی باید بگم و چه موقع باید بگم. تمام کمبودهایی که در روند یادگیری خودم در 15 سال قبل حس کردم توی این دوره جبران کردم.امیدوارم به عنوان یک هدیه اینو از من بپذیرید و خوشحال میشم بازخوردهای شما رو بدونم. لینک یوتوب دوره  https://www.youtube.com/watch?v=-EXBd1tORc8 </description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Thu, 06 Apr 2023 11:58:26 +0330</pubDate>
            </item>
                    <item>
                <title>آنچه از دات نت باید بدانید</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AF%D8%A7%D8%AA-%D9%86%D8%AA-xw02thih5gar</link>
                <description>کنفرانس امسال دات نت و اشاره چند باره به مفاهیم پایه دات نت باعث شد یک ویدیوی کوتاه و ساده از امکاناتی که دات نت بهمون میده تهیه کنم.در این ویدیو در مورد مفاهیم زیر به شکل خلاصه صحبت میکنم.معرفی انواع تمپلیت های دات نت - MVC - Razor Page - Web Api - Blazor - Minimal Apiمعرفی امکانات دات نت - Middleware - Razor - Dependency Injection - Logging - Model binding - Kestrelمعرفی سازوکارهای ارتباطی غیر از HTTP- gRPC- SignalRلطفا نظرات خودتون در مورد این ویدیو رو توی یوتیوب بنویسید و خوشحال میشم سابسکرایب کنید تا بتونید ویدیوهای جدید رو ببینید. https://www.youtube.com/watch?v=viytU3GBGPw لینک ویدیوهمینطور از طریق کانال تلگرام من میتونید مطالب مرتبط با برنامه نویسی رو بخونید و ببینید.لینک کانال تلگرام</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Fri, 17 Feb 2023 14:43:10 +0330</pubDate>
            </item>
                    <item>
                <title>معرفی Best Practice ها در Asp.Net Core : امنیت</title>
                <link>https://virgool.io/@mortezadalil/%D9%85%D8%B9%D8%B1%D9%81%DB%8C-best-practice-%D9%87%D8%A7-%D8%AF%D8%B1-aspnet-core-%D8%A7%D9%85%D9%86%DB%8C%D8%AA-sa2lvhgefgr5</link>
                <description>این مجموعه با نگاهی به ویدیوی آموزشی Best Practice در Pluralsight که توسط Steve Smith تهیه شده، گردآوری شده است. این مجموعه مقالات به شکل ویدیویی هم در کانال تلگرامم قرار میگیره. خوشحال میشم برای با خبر شدن از مقالات و نوشته ها و ویدیوهای آموزشی در این کانال حضور داشته باشید و نظراتتون رو بنویسید. https://t.me/mediapub_channelفهرست مقالات مجموعه معرفی Best Practice های دات نت :مقدمه : آشنایی با REST و پیاده سازی GET/POST/PUT/DELETEبخش اول : پیاده سازی Responseبخش دوم : مدل های apiبخش سوم : پیاده سازی api بخش چهارم : امنیت (همین مقاله)در این مقاله به مرور بهترین روش ها برای برقراری امنیت در یک پروژه دات نت می پردازیم.در مورد JWT صحبت میکنیم و راه حل های جایگزین توکن را مطرح میکنیم. پیاده سازی authorization و authentication را انجام میدهیم و در مورد CORS هم صحبت میکنیم.استفاده از JWTیکی از روش های رایج برای تامین امنیت api ها استفاده از jwt است. jwt سه بخش دارد که در تصویر زیر مشخص است که با نقطه از هم جدا شده اند :هر بخش از نوع Base64 است. بخش اول header است. بخش دوم payload است. بخش سوم signature است. نکته مهم در مورد jwt این است که اطلاعات encode شده اند و encrypt نشده اند. پس هرگز اطلاعات حساس و مهم را در jwt نگهداری نکنید.اگر قرار است خودتان JWT خودتان را درست کنید نیاز دارید که 256 بیت باشد. اگر از نوع استرینگ است طول 64 هگزادسیمال کاراکتری داشته باشد یا حداقل 43 کاراکتر حرف بزرگ و کوچک و عدد داشته باشد تا به هم ریختگی کافی برای غیرقابل نفوذ بودن را دارا باشد.کلمه secret که برای verify کردن signature استفاده میشود باید بین STS (secure toke server) و Api Server که توکن را وریفای میکند شر باشد. اگر از پروتکلی مثل OAuth استفاده میکنید سرور web api نیاز دارد که به STS دسترسی داشته باشد تا توکن را وریفای کند.فراموش نکنید رمز ها و اطلاعات مهم را در JWT ذخیره نکنید.به تصویر زیر نگاه کنید تا روش کار با پروتکلی مثل OAuth2 را متوجه شوید.کلاینت درخواست لاگین را به سرور یا سرویس مربوط به توکن ارسال میکند.با فرض اینکه این درخواست درست باشد و کاربر اعتبارسنجی شود پیغام موفقیت در پاسخ ارسال میشود.کاربر در یک api دیگر درخواست توکن ارسال میکند.در پاسخ به کاربر access token که شامل claim هاست ارسال میشود.کلاینت این access token را کش میکند.(برای ریکوئست های بعدی)در ریکوئست های بعدی این توکن به شکل هدر Authorization به سمت سرور ارسال میشود و توسط سرور درستی آن بررسی شده و اجازه دسترسی به کاربر داده میشود. در صورت عدم دسترسی به دلیل valid نبودن توکن پیغام 401 صادر میشود(UnAuthorized). درصورتی که توکن ولید باشد اما سطح دسترسی لازم توسط کاربر وجود نداشته باشد خطای 403 برگردانده میشود (Forbidden) . راه حل دیگر صدور پیغام 404 توسط سرور است که میتواند برای جلوگیری از نمایش جزئیات خطا به دلایل امنیتی مفید باشد.مثلا میخواهید به یک ریپازیتوری خصوصی گیتهاب دسترسی داشته باشید با اینکه login هستید اما خطای 404 میگیرید چون دسترسی به ریپازیتوری خصوصی ندارید.در asp.net core برای کار کردن با jwt چندین پکیج وجود دارد.مثلا Microsoft.AspNetCore.Authentication.JwtBearer که یک middleware برای دریافت و اعتبارسنجی توکن هاست.مثلا System.IdentityModel.Tokens.Jwt کتابخانه دیگریست که برای ایجاد و سریالایز کردن و ولیدیت کردن JWT استفاده میشود.به جز این دو پکیج های دیگری هم وجود دارند.برای حفاظت از api ها به شکل زیر عمل میکنیم. دو endpoint ساده در کنترلر زیر داریمهر دوی این endpoint ها اتریبیوت authorize دارند. در دومی Role نیز مشخص شده است. اگر بخواهیم بدون استفاده از توکن به این endpoint ها دسترسی داشته باشیم خطای 401 میگیریم.برای راه اندازی پروژه یک اکستنشن متد مینویسیم که jwt را معرفی کنیم. تنظیمات مربوط به jwt در اینجا تعریف شده و سرویس آن به کمک کد زیر به application اضافه میشود.در این تنظیمات مشخص کرده ایم که توکن چطور ولیدیت شود.یک متد لاگین داریم که اگر موفقیت آمیز صدا زده شود یک توکن به کلاینت برمیگرداند. هندلر لاگین حاوی متدهای زیر است:در خط 66 تا 74 آبجکتی میسازیم که محتوای توکن را مشخص میسازد:پراپرتی issuer : صادر کنندهپراپرتی audience : مخاطبپراپرتی calims : اطلاعات اضافی(توسط متد GetClaims درست میشود)پراپرتی expires :  زمان انقضاپراپرتی notBefore : زمان ایجادپراپرتی signingCredentials : نحوه انکریپشنمتد لاگین بر اساس موجود بودن نام کاربری و کلمه عبور یک توکن تولید میکند.در حقیقت مرحله Authentication باید توسط خود ما انجام شود یعنی نام کاربری و کلمه عبور را در دیتابیس سرچ میکنیم.مرحله Authorization توسط Jwt انجام میشود. یعنی توکنی در اختیار ما قرار میدهد که محتویاتی دارد و توسط آن توکن و با قراردادن آن در هدر ریکوئست ها میتوانیم دسترسی کاربر به api ها را مدیریت کنیم.کافیست توکن را به عنوان Bearer Token در ریکوئست های دیگر به شکل فوق وارد کنیم.درصورتی که توکن معتبر باشد و منقضی نشده باشد دسترسی به api های دیگر ممکن است:در صورتی که توکن برای یک api با Role مشخصی که مربوط به این توکن نیست استفاده شود خطای زیر را میبینیم :این یعنی اپلیکیشن ما را میشناسد ولی دسترسی به این بخش را نداریم.اگر توکن باطل شود خطای 401 میگیریم.سه بخش مربوط به این توکن توسط سایت jwt.io قابل رویت است.مواردی که در استفاده از JWT باید در نظر گرفتاز آخرین ورژن jwt استفاده کنید.قبل از استفاده حتما توکن را ولیدیت کنید(کاری که خودکار توسط اتریبیوت Authorize در دات نت انجام میشود)عمر توکن را ولیدیت کنید که باطل نشده باشد.بخش audience را چک کنید که توکن مربوط به api شما باشد.به کمک آنچه در TokenValidtionParameter به عنوان آپشن ها تعریف کرده ایم موارد فوق بررسی میشوند:مثلا فیلد aud مربوط به audience است که ولید بودن آن چک میشود، لایف تایم نیز true است پس ولید بودن آن چک میشود و بر اساس nbf و exp است.(not before و expired date). iss همان فیلد issuer است که طبق کانفیگ گفته شده ولید بودن آن چک شود.حمله کننده ها میتوانند از توکن جعلی استفاده کنند تا به api شما دسترسی پیدا کنند، مطمئن باشید که توکن expired نشده باشد و بعد از زمان nbf (not before) باشد. iss را چک کنید که توسط issuer مطمئن تولید شده باشد. توسط aud هم چک کنید که برای شما باشد.اگر از سرتیفیکیت به جای secret key استفاده میکنید Url ها مربوط به سرتیفیکت را محدود کنید در غیر اینصورت کاربران خرابکار از توکنی که با سرتیفیکیت خودشان درست شده با هر URL دیگری استفاده میکنند و ممکن است شما آنها را ولید کنید.از secret key قوی استفاده کنید. اگر خرابکارها به سکرت شما دست پیدا کنند میتوانند توکن های شما را دستکاری کنند و تاییده از شما بگیرند.کتابخانه jwt-cracker میتواند فقط ظرف کمتر از 2 ساعت به کمک یک لپتاپ secret key را پیدا کند! سکرت خود را به شکل alphanumeric و با طول بیش از 43 کاراکتر بسازید.از روش Asymetric key برای ولیدیشن یک توکن بین چند سرور استفاده کنید. مثل OpenId که از همین روش استفاده میکند.مثلا در یک سرویسی که از google account استفاده میکند sign in میکنید. گوگل از Private key برای تایید توکن استفاده میکند سپس اپلیکیشن از public key مربوط به گوگل برای تایید توکن استفاده میکند. فایده اصلی این روش این است که شما بین چند سرویس Secret Key را share نمیکنید تا ولیدیشن همه جا به یک شکل انجام شود.استفاده از jwt و refresh tokenبه بدنه خروجی لاگین نگاه کنید، یک access token داریم که برای دسترسی به ریسورسها از آن استفاده میکنیم و یک refresh token. بعد از مدت زمان expired مقدار access token قابل استفاده نخواهد بود و خطای 401 یا UnAuthorized دریافت میکنیم و باید دوباره لاگین کنیم.اما رفرش توکن به درد همین موقع میخورد. کلاینت میتواند آن را در جایی نگه دارد و بوسیله آن و از طریق صدا زدن متد refresh مقدار Access token جدید را دریافت کند.متد revoke نیز وجود دارد که میتوان یک refresh token را باطل کرد. در تصویر زیر میبینید که این متد برای یک refresh token صدا زده شده است و این رفرش توکن دیگر قابل استفاده نخواهد بود.با revoke کردن دیگر رفرش توکن قابل استفاده نخواهد بود و کاربر حتما باید لاگین کند. در تصاویر زیر متد RefreshToken و RevokeRefreshToken را میبینید.رفرش توکن ابتدا با متد TakeRefreshToken رفرش توکن را پاک میکند چون با صدا زدن این متد استفاده شده محسوب میشود. رفرش توکن جدید را به نام token بر میگرداند. در ادامه با کمک ایمیل کاربر را پیدا کرده و accessToken ساخته میشود. متد RevokeRefreshToken هم رفرش توکن جاری را پاک میکند.روش های جایگزین برای Authenticationاگر روی سیستم ویندوزی هستید از Windows Authentication میتوان استفاده کرد. این روش امنیت را در بسیاری از سناریوها ساده میکند و یک تنظیم حداقلی نیاز دارد.روش دیگر Cookie based authentication است که برای روش هایی مثل ماژول های واسط بین فرانت و بکند قابل  انجام است و به آن BFF هم میگویند.(مثلا پروژه های Blazor به کمک یک پروژه میانی Shared به منظور به اشتراک گذاری مدل ها میتوانند BFF خطاب شوند چون نوعی فراهم کننده دیتای مناسب برای فرانتند هستند). این روش را Token Handler هم میگویند که یعنی از توکن استفاده میکنند اما آن را به سمت مرورگر نمیفرستند. استفاده از پکیج Negotiate برای اضافه کردن اعتبارسنجی ویندوی به پروژهبعد از مرحله 1 و 2 در مرحله 3 توکن به سرور BFF ارسال میشود و این سرور آن را برای استفاده های بعدی نگه میدارد و کوکی را به سمت کلاینت میفرستند. کلاینت به کمک این کوکی به شکل اتوماتیک درخواست های بعدی را با کوکی هندل میکند و ریکوئست 5 را با کوکی میفرستد. حالا سرور BFF با توجه به کوکی، توکن مناسب را برای Api Call ارسال میکند.تعریف بیش از یک روش برای Authenticationبه کمک اتریبیوت میتوان مشخص کرد که از کدام روش میخواهیم برای authorize استفاده کنیم. در این روش هم کوکی و هم jwt تعریف شده است.میتوان چندین روش را برای یک نوع از Authentication هم تعریف کرد.اضافه کردن Middleware فراموش نکنید ترتیب اضافه کردم میدل ویرهای مربوط به اعتبار سنجی و هویت سنجی به شکل زیر است :مفهوم Authentication : کاربر چه کسی است؟ آیا میشناسیم؟ آیا در سیستم ثبت شده؟مفهوم Authorization : کاربر چه دسترسی هایی دارد؟ آیا مجوز دسترسی به api خاص را دارد؟مفهوم  Resource-Based Authorizationاگر بخواهیم مجوز دسترسی را بر اساس مقادیر ریسورس بدهیم باید Policy تعریف کنیم.در مثال فوق EditPolicy یک پالیسی است که بر اساس کلاس SameAuthorRequirement کار میکند که این کلاس به شکل زیر باید پیاده سازی شود.هندلری که از این کلاس استفاده میکند به شکل زیر است:مفهوم CORSاگر api شما در مرورگر استفاده میشود باید CORS را تنظیم کنید. Cross origin resource sharing در استاندارد RFC 6454 تعریف شده است و در تعریف مرورگر ها مجبورند که در هر وب پیج امکان ریکوئست زدن به یک آدرس ناهمسان(با domain های مختلف) دیگر با آدرس جاری را کنترل کنند. دو آدرس وقتی Origin یکسان دارند که host و port یکسان در آدرس آنها شناسایی شود. در این صورت مشکلی پیش نخواهد آمد. Cors به مرورگرها اجازه میدهد که یک پیج ریکوئست هایی به آدرسی ناهمسان با پیج بزند.در حقیقت CORS یک پارامتر امنیتی نیست بلکه باعث میشود امنیت صفحه بیش از پیش فراهم شود و یک صفحه فقط بتواند با api های مرتبط با خودش در ارتباط باشد.همچنین مجوز دسترسی باید در سمت سرور انجام شود و نه در جایی که استفاده میشود.پتنظیمات CORS در فایل Program.csدر تنظیمات یک یا چند آدرس را با یک نام پالیسی مشخص تفکیک و تعریف میکنیم. این آدرس دقیقا صفحه ایست که قرار است به api های ما دسترسی داشته باشد.سپس به شکل زیر آن را به پایپلاین اضافه میکنیم.به محل قرار گیری app.UseCors و ترتیب آن دقت کنید.اگر به تعریف پالیسی های مختلف نیاز ندارد و تنها یک جا از سایت شما استفاده میکند به شکل زیر تنظیمات را انجام دهید.پالیسی مربوط به CORS میتواند شامل سه بخش باشد.میتوان به ازای enpoint ها هم کانفیگ CORS تعریف کرد:به کمک دو اتریبیوت موجود در تصویر میتوان CORS خاصی را برای api فعال و غیرفعال کرد. در Minimal api هم به همین روش میتوان عمل کرد.استفاده از HTTPدات نت یک اتریبیوت به نام RequiredHttps دارد که برای ریکوئست های مربوط به مرورگر طراحی شده و برای APi نیست.میتوانید از کد 302 برای صدا زدن api هایتان با HTTP استفاده کنید (که یعنی باید به شکل HTTPS صدا زده شوند)بهتر است همه Http ها را ریجکت کنید و 400 برگردانید. در حقیقت پیشنهاد میشود فقط HTTPS را accept کنید و api های اپلیکیشن خود را در یک reverse proxy قرار دهید و فقط ریکوئست هایی که HTTPS را پشتیبانی میکنند فوروارد کنید.میتواند اپلیکیشن خود را جوری تنظیم کنید که فقط به ریکوئست های HTTPS گوش کند.یکی از راه ها استفاده از متد UseUrls در program.cs است.میتوانید برای اینکار و مشخص کردن startup url ها از environment variable ها استفاده کنید که در تصویر فوق مشخص هستند. این روش زمانی که با پابلیش به وسیله داکر ترکیب میشود بسیار ساده و سودمند است. جمع بندیدر این بخش در مورد مقدمات JWT آموختیم که چطور به طور مناسب از آن استفاده کنیم. همچنین در مورد روش های دیگر Authentication صحبت شد.در نهایت در مورد CORS و HTTPS هم نکاتی بیان شد.</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Thu, 08 Sep 2022 14:50:26 +0430</pubDate>
            </item>
                    <item>
                <title>معرفی Best Practice ها در Asp.Net Core : پیاده سازی Api</title>
                <link>https://virgool.io/@mortezadalil/%D9%85%D8%B9%D8%B1%D9%81%DB%8C-best-practice-%D9%87%D8%A7-%D8%AF%D8%B1-aspnet-core-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-api-sn1tswn1oxid</link>
                <description>این مجموعه با نگاهی به ویدیوی آموزشی Best Practice در Pluralsight که توسط Steve Smith تهیه شده، گردآوری شده است. این مجموعه مقالات به شکل ویدیویی هم در کانال تلگرامم قرار میگیره. خوشحال میشم برای با خبر شدن از مقالات و نوشته ها و ویدیوهای آموزشی در این کانال حضور داشته باشید و نظراتتون رو بنویسید. https://t.me/mediapub_channelفهرست مقالات مجموعه معرفی Best Practice های دات نت :مقدمه : آشنایی با REST و پیاده سازی GET/POST/PUT/DELETEبخش اول : پیاده سازی Responseبخش دوم : مدل های api بخش سوم : پیاده سازی api (همین مقاله)بخش چهارم : امنیتعلیرغم ظهور Minimal Api ها و پرکاربرد شدنشان در آینده نزدیک، اما استفاده از MVC هنوز یکی از راه های متداول برای تولید api است. فریم Asp Core MVC قابلیت های زیادی برای تولید Api های بهینه دارد. بخشی از این قابلیت ها توسط ابزارهای زیر قابل پیاده سازی است.وجود اتریبیوت ApiController : به کمک این اتریبیوت قابلیت های خوبی به Api های شما افزوده میشود.وجود اتریبیوت Routing : که با این قابلیت برای هر Api قابل پیاده سازی و معرفی آدرس دسترسی endpoint را دارید. وجود Model Validation : شما میتوانید با کمترین زحمت در قبال خطا در مدل ورودی خطای 400 به کاربر نمایش دهید. فکر کنید همین موضوع چقدر زحمت شما را برای مدیریت هر endpointکاهش اندازه کنترلر : یکی از نکاتیست که با رعایت نکات مرتبط با معماری درست به آن میرسیم و باعث نگهداری بهتر و عدم انباشتگی کدها در یک مکان میشود.استفاده از فیلترها برای اعمال Policy ها به اکشن ها دیگر قابلیت MVC است.پشتیبانی از async/await در اکشن ها نیز از قابلیت های MVC است.کش کردن خروجی نیز توسط MVC پشتیبانی میشود.فشرده سازی خروجی هم از قابلیت های MVC است که بدون زحمت در دسترس است.به کمک Content Negotiation میتوانید فرمت های مختلف خروجی خود را مدیریت کنید. در حقیقت کلاینت با هدر accept انتخاب میکند که چه فرمتی را نیاز دارد.میتوانید مدیریت خطا را به کمک MVC در حالت توسعه و حالت انتشار متفاوت پیاده سازی کنید.میتوانید تایپ خروجی را به کمک ActionResult&lt;T&gt; مشخص کنید و داکیومنت کردن api ها را ساده تر کنید.وجود قابلیت های ControllerBase به عنوان والد کنترلر و ایجاد کلاس والد دلخواد برای کنترلر با امکانات کاستوم خودتان از دیگر قابلیت های MVC است.کاهش اندازه کنترلربا کاهش کدهای داخل اکشن ها به این هدف میرسیم. باید &quot;منطق و محاسبات&quot; مربوط به اپلیکیشن را به بخش های مربوط به خودشان منتقل کرد. معمولا داخل اکشن ها پیاده سازی شرط و تصمیم ندارد. اگر مجبور به بررسی مواردی در اکشن ها باشید باید از Filter استفاده کنید. باید از نوشتن Business logic یا Data Access logic در داخل اکشن ها بپرهیزید. اگر اینها را رعایت کنید میبینید که چیز قابل ذکری برای پیاده سازی در اکشن ها باقی نمیماند!در اکشن های خودتان از نوشتن try/catch بپرهیزید. کنترل خطا هم نوعی مرتبط با شرط و تصمیم است که پیش از این گفتیم نباید در اکشن ها وجود داشته باشد. پیاده سازی try/catch برای تولید خطا در خروجی یک نوع anti pattern است. روش درست این است که سرویس های ما خطا را به شکل خاص تولید کنند و ریترن کنند و ما نیازی به تولید ریسپانس بر اساس exception نداشته باشیم.ایجاد یک کنترلر بزرگ باعث موارد زیر میشود:عدم انسجام کداحتمال قوی به بزرگتر شدن در طول زمانبیشتر شدن وابستگی ها و کوپل شدن endpoint ها به کتابخانه هااستفاده از Filter ها و MiddleWare هااگر به api های خود دقت کنید و تک تک endpoint ها و اکشن های خود را ببینید و کدهای مشترک فراوانی پیدا کنید، این شانس را دارید که تمام این کدها را refactor کرده و در فیلترها قرار دهید. اگر عملیات شما مرتبط با model binding یا چیزهای سطح پایین تر از فیچرهای MVC باشد میتوانید از middleware ها استفاده کنید. استفاده از filter و middleware باعث پایداری بیشتر api های شما میشوند.هرگز به حافظه توسعه دهنده ها برای رعایت توصیه ها در کد تکیه نکنید، به جای آن مجموعه ای از ضوابط را به کمک فیلترها و middleware ها به پروژه متصل کنید. مثلا ApiController که به شکل اتریبیوت روی کنترلر مینشیند یک فیلتر است که عملیات Model Validation را انجام میدهد و در ورودی اکشن ها ما را از استفاده از FromRoute یا FromBody و ... را بی نیاز میکند. یا ایجاد میدلویر برای Logging یا Exception ها از جمله مواردیست که میتواند به پایداری و تمیزی کد شما کمک کند.هر api به طور بالقوه میتواند پیشوند async داشته باشد، میتواند به دیتابیس وصل شود یا به api های دیگر پیام ارسال کند یا با فایل ها کار کند. به ندرت پیش می آید که یک api داشته باشید که نیاز به پیشوند async نداشته باشد.استفاده از [ResponseCache]از این اتریبیوت برای کش کردن ریسورس ها میتوان استفاده کرد. افزودن این اتریبیوت باعث اضافه شدن چیزی به هدر ریکوئست میشود که در سمت کلاینت یا intermediate proxy قابل استفاده است و تعیین میکند که چگونه response باید کش شود. دقت کنید که ریسپانس ها به طور پیشفرض روی وب سرور ذخیره نمیشوند تنها کار اولیه ای که این اتریبیوت با افزودن هدر کش میتواند انجام دهد بر اساس HTTP Caching با پروتکل RFC 7234 است. با این وجود اگر میخواهید ریسپانس ها را در حافظه سرور ذخیره کنید تا از مزیت عدم استفاده از دیتابیس و کوئری نگرفتن در مواقع مورد نیاز استفاده کنید.ما برای response cache از میدل ویر هم میتوانیم استفاده کنیم که با همین اتریبیوت ترکیب میشود. در این حالت باید از VaryByParam استفاده کرد تا ریسپانس متفاوت از یک ریکوئست بر اساس کوئری استرینگ را هندل کرد. مثلا اگر از کوئری استرینگ برای paging استفاده میکنید و صفحه 1 را کش کرده اید و کسی از شما صفحه 2 را میخواهد (کوئری استرینگ عوض شده) نباید این اشتباه رخ دهد که همان صفحه 1 را به فرد جدید نمایش دهید. چون اطلاعات در سمت سرور ذخیره میشود زمانی که توسعه به شکل چند سروری انجام میشود روش مناسبی نیست.این اتریبیوت به هر اکشنی اضافه شود به هدر ریسپانس اطلاعات کش را اضافه میکند.در تصویر بالا میبیند که مدت زمان کش را 10 ثانیه و محل کش را روی Any تنظیم کردیم. آنچه در هدر ریسپانس دیده میشود به شکل کادر بنفش رنگ است. با این قرارداد client و intermediate proxy میفهمند که کش را در کجا و در چه مدت زمانی انجام دهند.استفاده از Response Compressionبصورت پیش فرض بر اساس فیچرهای وب سرور فشرده سازی انجام میشود که از پیاده سازی فشرده سازی در middleware سریعتر است. مثلا IIS به طور پیش فرض از فشرده سازی پشتیبانی میکند.اگر قرار هست خودتان فشرده سازی را به عهده بگیرید از فشرده سازی ریسپانس های کوچک بپرهیزید چون دستاوردی ندارد و حتا ممکن است در بعضی الگوریتم ها علاوه بر صرف زمان نتیجه فشرده سازی حجم بیشتری از دیتای اولیه داشته باشد.بهتر است زیر 1000 بایت را بیخیال شوید.به کمک هدر Accept-Encoding کلاینت باید به سرور اطلاع دهد که از چه Enodingی پشتیبانی میکند. سرور نیز باید بر اساس این Encoding و آنچه استفاده کرده است هدر Content-Encoding را در ریسپانس تولید کند.برای HTTPS موضوعات امنیتی برای فشرده سازی باید لحاظ شود.تنظیمات مربوط به فشرده سازی ریسپانس،تنظیمات مربوط به  Content Negotiationاگر محتوای متفاوتی قرار باشد ریترن شود باید تنظیمات کوچکی روی پروژه MVC انجام دهید. مثلا در این کد میبینیم که هدر ریکوئست از سمت کلاینت باید مورد توجه قرار گیرد همچنین اکستنشن متد AddXmlSeriaizerFormatter اضافه شده است.(علاوه بر حالت پیشفرض جیسون) وقتی پارامتر Accept هدر روی xml است خروجی xml است.وقتی پارامتر Accept را چیزی ست نکنیم حالت پیشفرض جیسون در خروجی نمایش داده مشودتنظیمات مربوط به Exception Handlingدر هنگام بروز خطا، معمولا در حالت پروداکشن اطلاعات کمتری به سمت کلاینت ارسال میشود چون اطلاعات خطا برای توسعه ضروریست و ممکن است در معرض دید قرار گرفتن آن برای همه، اطلاعات ناخواسته ای از سیستم را منتشر کند.ما در این تصویر یک هندلر با دو آدرس متفاوت برای هندل کردن خطاها ایجاد کرده ایم که نحوه نمایش هر کدام متفاوت خواهد بود.برای حالت پروداکشن خطا هیچ دیتیلی را برنمیگرداند. روت تعریف میکنیم و در سوئگر هم نمایش نمیدهیم.خروجی توسط Problem چیزی شبیه به این تصویر خواهد بود. به کمک traceId میتوانید ریکوئست را در لاگ های خود پیدا کنید.برای دریافت جزئیات بیشتر  از همان رویکرد میتوانید استفاده کنید اما با endpoint متفاوت. داخل اکشن چک میکنیم که حتما داخل محیط development هستیم، اگر نبودیم NotFound میدهیم تا کسی به طور مستقیم یا غیر مستقیم این اکشن را در حالت پروداکشن صدا نزند.این اکشن از IExceptionHandlerFeature که به طور Built-in در دات نت وجود دارد استفاده میکند تا StackTrace و Message مربوط به exception را نمایش دهد.(دقت کنید که ریترن ما Problem باید باشد)در حالت development خروجی اطلاعات کاملی نمایش میدهد. title همان message است و detail همان StackTraceپیاده سازی الگوی Mediator توسط MediatRیکی از کتابخانه هایی که به ما کمک میکند تا کنترلر سبک تری داشته باشیم MediatR است.الگوی Madiator یکی از 23 الگوی طراحی در شی گرایی است که در کتاب Design Pattern  منتشر شده در 1994 توضیح داده شده است.اصطلاح Gang of Four برچسب گروهی بود که شامل اریک گاما (Erich Gamma)، ریچارد هلم (Richard Helm)، رالف جانسون (Ralph Johnson) وجان ولیسیدس (John Vlissides) میشد. این افراد نویسندگان کتاب مهندسی نرم‌افزار  Elements of Reusable Object-Oriented Software بودند.روشی برای مستقل کردن آبجکت ها از هم به کمک یک واسط ما یک آبجکت MediatR میسازیم که بین آبجکت صدا زده شده(اکشن) و آبجکت تولید کننده ریسپانس(هندلر) مسیج رد و بدل میکند.این کتابخانه به این شکل عمل میکند که به ازای هر ریکوئست یک اینترفیس جنریک از تایپ دیتای ورودی (یا تغییر یافته آن) نیاز دارد. همچنین به یک هندلر جنریک با دو تایپ TReqeust و TResponse نیاز دارد. کلاس TRequest باید بر اساس IRequest پیاده سازی شود.پس ما یک کلاس به عنوان Request و یک کلاس به عنوان Request Handler داریم.وقتی application راه اندازی میشود MediatR به کمک reflection از روی assembly تمامی این کلاس ها را پیدا میکند یعنی تمامی کلاس هایی که IRequest را پیاده سازی کرده اند. همچنین به کمک dependenct injection در جاهایی که IMediatR اینجکت شده است سرویس مورد نظر را در Program.cs تعریف میکند.به کمک متد send این سرویس میتواند چیزی از جنس IRequest دریافت کند که طبیعتا تمامی endpoint های ما باید کلاسی از روی این اینترفیس به عنوان ورودی پیاده سازی کنند.کتابخانه به کمک همان رفلکشن، هندلر مرتبط را می یابد و آبجکت ورودی را به آن پاس میدهد. (با invoke کردن handler)در نهایت ریترن ما چیزی از جنس TResponse است که به اکشن برگردانده میشود.اگر به سختی متوجه مطلب شدید یا نشدید! نگران نباشید؛ چند خط پایین تر با مثال این موضوع را خواهید دید.اگر علاقمند به این موضوع هستید مجموعه مقالات Clean Architecture را مطالعه کنید. در این مجموعه کار با MediatR توضیح داده شده است.با این روش هیچ وابستگی به شکل Compile time بین اکشن و هندلری که صدا زده میشود وجود ندارد و همه چیز runtime اتفاق می افتد.این کتابخانه برای notification نیز روش مشابه ای دارد با این تفاوت که ریترنی بر نمیگرداند.(حتا چندین هندلر میتوانند نوتیفیکیشن را هندل کنند)یک اکشن معمولی چطور باید پیاده سازی شوددر اکثر endpoint هایی که با data سروکار دارند و به عنوان web api شناخته میشوند رفتار مشترک تغییر این اطلاعات است و معمولا پیاده سازی مراحل زیر را دارد :اول : یک مدل ورودی داریم که باید validate شود. این بررسی به شکل رایگان و Built-in در web api وجود دارد، کافیست اتریبیوت ApiController را در بالای کنترلر بنویسیم.دوم : مدل ورودی باید به یک دیتامدل جدید و قابل استفاده برای هندلر تبدیل شود. ما نباید آنچه مربوط به Business  است در معرض عموم قرار دهیم یعنی Business Model ما باید از روی مدل ورودی ساخته شود و مدل ورودی بهینه ترین حالت برای گرفتن اطلاعات است (نه اطلاعات زیاد و نه کمتر از حد معمول)سوم: کار واقعی مدل اینجا شروع میشود، آیا تغییری در سیستم توسط این مدل باید اعمال شود؟ آیا متدی باید صدا زده شود؟ چیزی باید ذخیره شود؟ وضعیت ها باید تغییر کنند؟ همه این چیزها در همین مرحله اتفاق می افتد. چهارم : مدل مورد نیاز به عنوان Response باید تولید شود. اگر عملیات موفق بود و نیازی به دیتا نبود نیاز به ارسال Business model نیست.پنجم : یک پاسخ مناسب در بستر HTTP بر اساس مدل ایجاد شده در مرحله قبل به بیرون ارسال میشود.تعداد کمی از این مراحل 5 گانه در خود اکشن اتفاق می افتد. مخصوصا اگر از MediatR استفاده کنید.حتا اینجکت سرویس های مختلف در کنترلرهای مختلف در این روش اتفاق نمی افتد.مثال برای یک Refactor سادهیک اکشن داریم که آبجکت newAuthor را به شکل HttpPost دریافت میکند و سپس در خطوط 44 تا 48 آن را به یک آبجکت Author (این آبجکت از تایپ های Domain است ) مپ و در خط 50، مدل را ذخیره میکند. در نهایت در خط 52 آنچه به عنوان response مورد نیاز است ایجاد کرده و در خط 53 همان را ریترن میکند.به کمک کتابخانه MediatR کد اکشن را به شکل زیر اصلاح میکنیم. برای استفاده از MediatR باید آن را به کنترلر اینجکت کنیم، میتوانیم یک BaseController به شکلی که میبینید بسازیم و پراپرتی آن را در آنجا تعریف کنیم که نیاز به اینجکت کردن در هر کنترلر به شکل جدا نباشد.در اکشن ابتدا به کمک مدل ورودی، آبجکت Command را که مورد استفاده‌ی هندلر MediatR است میسازیم.سپس به کمک متد send این آبجکت را به عنوان ورودی پاس میدهیم. چون جنس این آبجکت مشخص است و معلوم است از چه اینترفیسی ارث بری کرده است، MediatR میداند هر اینترفیس مرتبط با کدام هندلر است (این دانایی از رفلکشن موجود در program.cs آمده است!)خروجی هندلر مورد نظر نیز به عنوان خروجی api به بیرون فرستاده میشود. استفاده از Api Endpoint ها میتوان به جای استفاده از مفهوم کنترلر و اکشن مجموعه ای از endpoint ها معرفی کرد و ارتباط مدل بیرونی با بیزنس داخلی را نزدیک تر کرد.یک پکیج به نام Ardalis.ApiEndpoint داریم که کلاس های پایه ای را برای اینکار فراهم میکند.حالت جنریک به شما این امکان را میدهد که هر نوع ریکوئستی را به هر نوع تایپی منتهی کنید.اساس این پکیج، الگوی PEPR است.همانطور که میدانید MVC براساس سه کلمه Model-View-Controller است و برای ایجاد یک پروژه به همراه طراحی رابط کاربری مناسب است. در زمانی که شما Api میسازید هیچ ویویی در کار نیست. یعنی الگوی شما MC میشود یعنی Model-Controller یا مثلا Model-Action-Controller که میشود الگوی MAC!!نکته اینجاست که شما باز هم از MVC استفاده نمیکنید پس چرا به الگوی بهتری به جای این الگوی نصفه فکر نکنیم؟Api Endpoints یک ویژگی مناسب برای اینکار است که این قابلیت در دات نت وجود دارد. الگوی REPR سه قسمت دارد :بخش Request :  شکلی از دیتا که endpoint نیاز دارد.بخش EndPoint : منطقی که endpoint روی request پیاده میکند.بخش Resonse : پاسخی که endpoint به سمت صدا زننده بر میگرداند.این مقاله را ببینیدبا این روش هر endpoint در api های شما متناظر با یک فایل یا کلاس endpoint میتواند باشد، که هندلر در آن قرار دارد.به کمک ویژگی های Visual Studio در اهمیت دادن به نامگذاری ها میتوانید جوری api های خود را نامگذاری کنید که گروه بندی شوند. با کمک prefix این اتفاق می افتد مثلا api مربوط به Create میتواند به شکلی که میبینید دسته بندی شود. دسترسی به Dto ها هم راحت تر است و در کنار endpoint مرتبط با خودشان هستند.فایل Create.csدر تصویر بالا فایل endpoint مربوط به عملیات create را میبینیم. کل endpoint را در یک صفحه میبینید. (بر خلاف MVC که اکشن و هندلر دو بخش جدا از هم بودند)هر Endpoint باید از کلاس EndpointBaseAsync ارث بری کند.همچنین برای ریکوئست و ریزالت دو تایپ WithRequest و WithActionResult تعریف شده که حتا از NoResult و NoRequest هم پشتیبانی میکند.داخل HandleAsync رفتاری شبیه به هندلر MediatR داریم.مثال دیگری ببینیم.Update.csآپدیت تفاوت چندانی با Create ندارد فقط در HttpPut ما پارامتر id را دریافت کردیم. این id در آبجکت request هم وجود دارد که توسط همان پارامتر پر میشود. این ویژگی در MVC و Web api وجود نداشت.Update.UpdateAuthorCommand.csهمانطور که میبیند Id وجود دارد و از طریق Routing پر میشود و Required است.از اتریبیوت JsonIgnore هم استفاده کردیم که در سوئگر دیده نشود.آنچه در swagger دیده میشود به شکل زیر استنکته کلیدی در استفاده از پکیج هایی مثل fast endpoint یا minimalApi.Endpoint این است که میتوانید endpoint های خود را دسته بندی کنید.راه حل جایگزین برای MVC ایجاد Minimal Apiبا تصویر زیر به سادگی این روش مشخص است:در خروجی minimal api باید تایپ IResult برگرداند که پیاده سازی های متنوعی دارد.میتوان از اتریبیوت های مرتبط با سوئگر هم در minimal api ها استفاده کرد.مشکل بزرگ minimal api این است که مایکروسافت یک داکیومنت خوب برای سازماندهی آنها تهیه نکرده است. طبیعتا قراردادن ده ها یا صدها مینیمال ای پی آی درون program.cs کار درستی نیست. فایل های بزرگ برای سازماندهی و پیدا کردن بخش های مورد نظر آزاردهنده هستند. همچنین کار تیمی روی پروژه ای که فایل های بزرگ دارد مشکل است.(به دلیل تداخل ها هنگام merge و commit)باید بتوانیم از فولدرها و فایل ها برای سازماندهی endpoint ها استفاده کنیم. فولدرها متناظر با endpoint ها باید باشند.به طور ایده ال بهتر است هر endpoint فایل خودش را داشته باشد.به دو روش میتوان این سازماندهی را انجام داد:1. استفاده از پکیج MinimalApi.Endpoint2. استفاده از Extension Methodsمینیمال api ها از داخل program.cs ستاپ میشوند.در تصویر فوق دو minimal api میبینیم کی در خط 53 و دیگری در خط 58 که تنظیمات اضافی متادیتاهای سوئگر را دارد.api های واقعی ظاهری شبیه تر به همان خط 58 یعنی delete دارند. یعنی طولانی تر هستند. بدیهیست تعداد خطوط فایل program.cs بیش از 1000 خط میشود اگر با یک اپلیکیشن واقعی سروکار داشته باشیم. چطور میتوان این خطوط را به فایل های مستقل endpoint تقسیم بندی کرد؟یکی از راه ها استفاده از کتابخانه MinimalApi.endpoint است. این کتابخانه در خط 43 تصویر زیر قابل مشاهده است.با این روش تمامی endpoint هایی که اینترفیس IEndpoint را پیاده سازی کرده باشند داخل بازی محسوب میشوند. به اینترفیس ها نگاه کنید ورودی های جنریک آن نشان میدهد که مدل CreateAuthorRequest را میگیرد و IResult را برمیگرداند.دو متد را با توجه به این اینترفیس ها باید پیاده سازی کنیم. AddRoute و HandleAsyncدر اولین متد عملیات مپ کردن به متد درست Http به همراه آدرس انجام میشود. همچنین آبجکت ریکوئست و اعمال دیپندنسی در این بخش انجام میشود.در متد دوم عملیات اصلی Endpoint پیاده سازی میشود.راه دیگر برای جداسازی endpoint ها و قرار دادن آنها در فایل جدا استفاده از extension method هاست.با این روش برای هر endpoint متد و کلاس مربوط به خودش در فایل جدا خواهیم داشت.میتوانید به جای اینکه برای تک تک endpoint ها اکستنشن جدا معرفی کنید، آنها را دسته بندی کنید و مثلا آنهایی که مربوط به Author هستند در یک اکستنشن متد قرار دهید.بطور کلی شکستن endpoint ها به فایل های جدا برای مدیریت آنها روش درستی است. با نگاه کردن به فایل ها به راحتی متوجه میشوید کدام endpoint ها پیاده سازی شده و چه چیزهایی باقیماندهاست.اضافه کردن Background Service ها به Web Apiزمانی که از یک message bus اکسترنال یا یک صف برای برقراری ارتباط با سرویس های دیگر استفاده میکنید دات نت کور به شکل Built-in میتوند از روش Background Service در کنار پروژه Api شما استفاده کند.از AddHostedService استفاده خواهیم کرد و نیازی به اضافه کردن پکیج جدید از Nuget نیست.روش کار به این شکل است که یک سرویس به شکل تایپ تعریف میکنید که از تایپ BackgroundService ارث بری کند. حالا متد ExecuteAsync را پیاده سازی میکنید. در این متد میتوانید Delay خود را معرفی کنید که فواصل زمانی را کنترل کند. یعنی در چه بازه های زمانی عملیات درون این متد تکرار شوند.در مثال بالا ما یک worker به نام DataConsistencyWorker تعریف کرده ایم که بطور پریودیک دیتابیس را برای داده های ناپایدار چک میکند و اگر چیزی نیاز به اصلاح داشت اصلاح میکند.چون قرار است با دیتابس کار کنیم باید از DbContext و Repository استفاده کنیم.در خط 41 سرویسی به شکل Scope تنظیم شده که این بک گراند سرویس با آن کار کند.کانستراکتور IServiceProvider را میگیرد و به سرویس ها میتواند دسترسی داشته باشد. بک گراند سرویس ها به طور Singleton در اپلیکیشن وجود دارند یعنی فقط یک نمونه از آنها در زمان شروع اپلیکیشن ساخته میشود و در دفعات بعدی که سرویس بک گراند اجرا میشود یک نمونه جدید از سرویس نخواهیم داشت.در داخل متد DoWork میبینیم که از آبجکت Services که از IServiceProvider ساخته شده استفاده شده است.سرویسی که از آن استفاده شده به شکل زیر است:کاری که این متد میکند این است که از دیتابیس تمامی نام کاربری هایی که ولید نیستند بیرون کشیده و اصلاح میکند. و اینکار را هر 30 ثانیه تکرار میکند.جمع بندیدر این مقاله مطالب زیر را مرور کردیم :با قابلیت های MVC آشنا شدیم.با MediatR آشنا شدیم که باعث شد کنترلر های ما کوچک تر شوند و منطق برنامه در جایی دیگر و در هندلر خودش کپسوله شود.با الگوی REPR آشنا شدیم و فهمیدیم که برای api ها الگوی MVC بخش View را ندارد و این الگو جایگزین مناسبی برای MVC است.با Minimal Api ها ونحوه سازماندهی آنها به کمک اکستنشن متد و MinimalApi.Endpoint آشنا شدیم.در نهایت هم با Background Service ها مقاله را پایان دادیم.</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Sat, 27 Aug 2022 12:41:38 +0430</pubDate>
            </item>
                    <item>
                <title>ترمینال خود را بدون درد و خونریزی زیبا کنید</title>
                <link>https://virgool.io/@mortezadalil/%D8%AA%D8%B1%D9%85%DB%8C%D9%86%D8%A7%D9%84-%D8%AE%D9%88%D8%AF-%D8%B1%D8%A7-%D8%A8%D8%AF%D9%88%D9%86-%D8%AF%D8%B1%D8%AF-%D9%88-%D8%AE%D9%88%D9%86%D8%B1%DB%8C%D8%B2%DB%8C-%D8%B2%DB%8C%D8%A8%D8%A7-%DA%A9%D9%86%DB%8C%D8%AF-yxfud4s2rdao</link>
                <description>پکیج oh my posh (وب سایت ) را از طریق دستور زیر نصب کنید : Install-Module oh-my-posh -Scope CurrentUserتم ها را بگیرید : Get-PoshThemesمسیر دانلود تم ها پس از اجرا به شما گفته میشود.(مثلا  C:\Users\MY COMPUTER\AppData\Local\Programs\oh-my-posh\themes)اگر دو دستور بالا به هر دلیلی با خطا مواجه شد از دستور زیر استفاده کنید :winget install JanDeDobbeleer.OhMyPosh -s wingetدر ترمینال دستور زیر را بنویسید تا فایل پروفایل باز شود.notepad $profileدر فایل پروفایل خطوط زیر را بنویسید و ذخیره کنید :Import-Module oh-my-poshoh-my-posh prompt init pwsh --config &amp;quotC:\Users\MY COMPUTER\AppData\Local\Programs\oh-my-posh\themes\dracula.omp.json&amp;quot | Invoke-Expressionآدرس داخل دابل کوتیشن مسیریست که به تم شما ختم میشود. تم ها را در مرحله دوم در این مسیر دانلود کردیم. برای مشاهده تم ها از این لینک استفاده کنید.پس از ذخیره فایل پروفایل، پاورشل خود را یکبار ببندید و باز کنید. ممکن است با خطای زیر روبرو شوید.در این صورت دستور زیر را بنویسید.Set-ExecutionPolicy -ExecutionPolicy Unrestrictedبا بستن و بازکردن دوباره پاورشل تصویر زیر را با فونت به هم ریخته میبینید.به سایت nerdFonts رفته و فونت دلخواه را دانلود کنید (من فونت CaskaydiaCove را انتخاب و در مسیری دلخواه دانلود و به ویندوز اضافه میکنم)حالا در هدر پاورشل رایت کلیک کنید و در بخش Properties نوع فونت خود را تغییر دهید(بر اساس آنچه دانلود کردیم گزینه CaskaydiaCove NF را انتخاب میکنم) با باز و بسته کردن پاورشل تصویری شبیه به عکس زیر خواهید دید.ممکن است در ترمینال ویژوال کد آیکن ها درست نشان داده نشود. کافیست در ویژوال کد با توجه به تصویر زیر فونت خود را تغییر دهید.در ترمینال ابتدا روی علامت مثبت کلیک کرده و configure Terminal Settings را انتخاب میکنیم. در پنجره باز شده عبارت font را مینویسیم و در بخش font family اسم فونتی که دانلود کرده بودیم را مینویسیم. از طریق edit in settings.json هم میتوان این تنظیمات را انجام داد.ترمینال را یکبار میبندیم و دوباره باز میکنیم.و تمام.</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Sat, 13 Aug 2022 12:08:40 +0430</pubDate>
            </item>
                    <item>
                <title>کدنویسی آنلاین پروژه حاضرجواب</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D9%85%D9%82%D8%AF%D9%85%D8%A7%D8%AA%DB%8C-%D8%AC%D8%A7%D9%88%D8%A7%D8%A7%D8%B3%DA%A9%D8%B1%DB%8C%D9%BE%D8%AA-javascript-zntxwiyy7prn</link>
                <description>این پنج شنبه (20 مرداد 1401) ساعت ۹:۳۰ صبح میخوام به پروژه کوچیک جاوااسکریپتی رو به شکل لایو برای دوره برنامه نویسی مقدماتی پیاده کنم.پروژه خیلی ساده هست. اگر از خیلی قدیم کامپیوتر دارید و با مدل ۳۸۶ و ۴۸۶ و پنتیوم شروع کرده باشید برنامه ای به زبان فارسی تحت DOS به نام «کلنجار» را به خاطر دارید.این برنامه با کاربر حرف میزد، البته به شکل نوشتاری. دیتابیس کوچکی با پاسخ های متنوع داشت. شما از برنامه سوال میکردید و برنامه به شما جواب می‌داد.قصد دارم این برنامه ساده رو برای کسانی که برنامه نویسی بلد نیستند با جاوااسکریپت پیاده کنم.دستورات فوق العاده ساده هستند در حدی که از کنجکاوی علاقمندان مبتدی نسبت به برنامه نویسی می‌کاهد و شاید علاقمندشان هم بکند.اگر دوست دارید در این رویداد و کد نویسی های لایو دیگر هم شرکت کنید کانال تلگرامم را دنبال کنید. لینک شرکت در دوره بدون ثبت نام در کانالم قرار داده شده.https://t.me/mediapub_channel</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Wed, 10 Aug 2022 09:05:47 +0430</pubDate>
            </item>
                    <item>
                <title>معرفی Best Practice ها در Asp.Net Core : مدل های Api</title>
                <link>https://virgool.io/@mortezadalil/%D9%85%D8%B9%D8%B1%D9%81%DB%8C-best-practice-%D9%87%D8%A7-%D8%AF%D8%B1-aspnet-core-%D9%85%D8%AF%D9%84-%D9%87%D8%A7%DB%8C-api-so1e2u19dy0a</link>
                <description>این مجموعه با نگاهی به ویدیوی آموزشی Best Practice در Pluralsight که توسط Steve Smith تهیه شده، گردآوری شده است. این مجموعه مقالات به شکل ویدیویی هم در کانال تلگرامم قرار میگیره. خوشحال میشم برای با خبر شدن از مقالات و نوشته ها و ویدیوهای آموزشی در این کانال حضور داشته باشید و نظراتتون رو بنویسید. https://t.me/mediapub_channelفهرست مقالات مجموعه معرفی Best Practice های دات نت :مقدمه : آشنایی با REST و پیاده سازی GET/POST/PUT/DELETEبخش اول : پیاده سازی Responseبخش دوم : مدل های api (همین مقاله)بخش سوم : پیاده سازی apiبخش چهارم : امنیتمدل های موجود در Web Api معمولا DTO هستند. DTO ها آبجکت های بدون Behavior هستند که فقط state را مشخص میکنند، یعنی فقط Property دارند. برخی DTO را با POCO اشتباه میگیرند.(Plain Old CLR C# Object )همه DTO ها POCO هستند(DTO ها از System.Object به طور پیش فرض ارث بری میکنند) ولی همه POCO ها DTO نیستند چون میتوانند Behavior داشته باشند یعنی حاوی متد باشند.در این مقاله منظور از مدل همه مدلهایی که استفاده میکنیم نیست بلکه مدلهاییست که در ورودی یا خروجی api استفاده میشوند و کلاینت به معنی برنامه نویس Front-End با آنها تعامل دارند.چند ویژگی مهم و پایه مدل ها :- تمرکز مدل ها روی انتقال اطلاعات است.- مدل ها یا کلاس هستند یا record.- مدل ها با اینکه حاوی متد نیستند اما میتوانند ولیدیشن را به روش Data Annotation هندل کنند. این یک خاصیت درونی در دات نت است و به کمک اتریبیوت ApiController امکان کنترل ولیدیشن را به ما میدهد.- نامگذاری DTO ها بر پایه Entity ها نیست. مثلا CustomerDTO یا AuthorDTO نامگذاری مناسب نیستند. نامگداری مناسب متاثر از کارکرد DTO است، مثلا CreateAuthorRequest یک نامگذاری مناسب است برای یک کارکرد خاص و در جای دیگر DTO دیگر با کارکرد دیگر لازم است.به نام مناسب این DTO نگاه کنید. شامل سه پراپرتی است. چون DTO هست هیچ Encapsulationی ندارد یعنی Protected یا Private یا Internal نیست. به مثال فوق نگاه کنید:اولا نامگذاری مناسب استفاده شده و صرفا به AuthorDTO بسنده نشده چون کاملا کارکرد مبهمی دارد.ثانیا همه پراپرتی ها به شکل پابلیک هستند.ثالثا چون هدف ایجاد یک ریسورس است و کلمه Command داریم از شناسه یا Id در پراپرتی ها استفاده نشده است چون در سمت سرور تولید میشود.رابعا با اینکه این کلاس یک DTO است و قرار نیست Behavior داشته باشد اما استفاده از Annotation ایرادی ندارد. ضروری بودن یک پراپرتی یا محدودیت در تعداد کاراکتر ها از این روش امکانپذیر است.خامسا از ثابت ها برای ارقام مربوط به MaxLength استفاده کردیم که قابلیت تغییر در یکجا و تاثیر در همه جا را ایجاد کرده باشیم.میتوانیم از ویژگی جدید سی شارپ یعنی record برای ایجاد DTO استفاده کنیم.  ظاهر باید تعداد خطوط کد در روش record کمتر شود ولی چون Data Annotation داریم چندان فرقی با حالت قبل ندارد. طبیعتا برای DTO هایی که در خروجی یا بین لایه ها استفاده میشوند این روش ساده تر است.قانون Postelدر آنچه ارسال میکنی سخت گیر و در آنچه دریافت میکنی آسانگیر باش.واضح است وقتی برنامه شما دیتایی به برنامه دیگری ارسال میکند باید دقیقا بر اساس آنچه آن برنامه میخواهد و به شکل اینترفیس در اختیار ما قرار داده دیتا را ارسال کرد. اما وقتی برنامه های دیگر، برنامه شما را صدا میزنند باید بتوانید دیتای ریکوئست هایی که کاملا دقیق و بر مبنای مدل ورودی نیستند نیز دریافت کنید.بهتر است api های شما آسانگیر باشند و خطاهای کمتری نمایش دهند.قانون Hyrumوقتی Api شما کاربران زیادی دارد مهم نیست که برای api چه قراردادی در دریافت یا پاسخ قرار داده اید، آنچه مهم است رفتار عینی کاربران و نحوه تعامل آنها با api است. سیستم شما بستگی به نحوه استفاده دیگران دارد. مثال : فرض کنید api برای توئیتر را نوشته اید و میخواهید alias برای کاربر دریافت کنید. همچنین api شما برای مدتی نیز استفاده شده است و فرمت های مختلفی برای نمایش alias وجود دارد.آیا راه حل استفاده از یک مدل ولیدیشن با محدودیت زیاد است؟آیا اینکه همه باید با علامت @ نام کاربری خود را به عنوان alias بفرستند یک آسانگیری است؟طبیعتا این روش کلاینت هایی را که انتظار ندارند همیشه از @ استفاده کنند با خطا مواجه خواهد کرد. پس استفاده از Regular expression به این شکل کار صحیحی نیست. به علاوه اینکه معمولا خطای صادر شده توسط Regular expression مبهم است.پس دو مشکل داریم یکی عدم انعطاف پذیری در قبول alias و دیگری پیام خطای نامناسب برای کاربر.راه حل برای رعایت قانون Postel این است که تمامی فرمت های alias را قبول کنیم ولی در کد به یک مدل واحد تبدیل کنیم. در این حالت api همیشه درست کار میکند و کلاینت ها با خطا روبرو نمیشوند.روش فوق پیاده سازی همین ایده است اما بدون در نظر گرفتن پرفورمنس و تمیزی کد.چه دیتایی توسط API باید برگردانده شود؟دیتایی که در اختیار کلاینت قرار میگیرد نباید خیلی بزرگ و نباید خیلی کوچک باشد. بزرگ بودن خروجی باعث مصرف کردن دیتای اضافی در شبکه و کند شدن میشود. دیتای کم نیز ممکن است کلاینت را مجبور به فراخوانی چندین api کند تا بتواند مجموعه دیتای مورد نیاز خود را بدست آورد.اما چطور بفهمیم کلاینت برای فراهم کردن دیتای مورد نیاز یک صفحه به چندین api نیاز دارد؟با در نظر گرفتن caching و data storage میتوان گفت آنچه کلاینت نیاز دارد شاید از پیش در اختیار داشته باشد.  در مرحله بعد اگر هیچ دیتایی نداشته باشد پاسخ این سوال 1 است.اگر فقط یک کلاینت داشته باشیماگر تنها یک Client داشته باشید بهتر است api خود را بر اساس نیاز این تک کلاینت طراحی کنید. یعنی از الگوری BFF استفاده کنید (Backend For Frontend) . در این روش مبنای طراحی بر اساس نیازهای کلاینت است. هر صفحه در بخش کلاینت مجموعه ای از دیتا نیاز دارد که ممکن است توسط بخش های مختلف از Backend قابل تامین باشد.متدهایی که کلاینت از آنها استفاده نمیکند را expose نکنید.تا جایی که ممکن است عملیات Data Aggregation را در سمت سرور انجام دهید که کلاینت دقیقا دیتایی را دریافت کند که به آن نیاز دارد.اگر کلاینت های زیادی داشته باشیم(یا public api باشد)برای هر ریسورس خود حالت summary , detailed داشته باشید. حالت summary را در api هایی که list ارائه میدهند استفاده کنید. حالت detailed را به شکل api های مستقل طراحی کنید.ریسورس هایی که به شکل کالکشن قرار است در لیست به کلاینت ارائه شوند قابلیت sort و filter داشته باشند و یک فرمت استاندارد داشته باشند.برای حالت های کالکشن هایی که sub-collection دارد تصمیم بگیرید که به چه شکل نمایش دهید. آیا به شکل لیست کامل نمایش داده شوند یا برای آنها نیز URI جایگزین کنیم؟این یک api برای گیتهاب و مربوط به اطلاعات کاربر است. دقت کنید که به جای نمایش لیست بلند از فالوررهای یک کاربر از آدرس ریسورس آن استفاده شده است.این نتیجه api مربوط به فالورهای یک user است , بار دیگر دیتیل ها به جای خود دیتا ها از URI تشکیل شده اند و ما دیتا را در اینجا به شکل Summary میبینیم.بصورت پیش فرض تنها 30 رکورد هر بار در لیست وجود دارد.همانطور که میبینید اطلاعات مربوط به pagination در خروجی وجود ندارد. این اطلاعات در هدر خرجی است.اطلاعات صفحه قبل و بعد در بخش هدر خروجی وجود دارد. که نویگیشن لینک ها هستند.در برخی api ها اطلاعات مربوط به pagination در بدنه خروجی وجود دارد.ملاحظاتی برای طراحی api زمانی که pagination دارند باید لحاظ شود:آیا اصلا نیازی به pagination در سمت BackEnd هست؟ اگر تعداد رکوردهای خروجی زیاد باشد نیاز است. بهتر است از ابتدا پیش بینی pagination را برای api در نظر گرفت.اطلاعات مربوط به pagination را در بدنه خروجی قرار میدهید یا در هدر آن؟ طبق توصیه RFC 5988 بهتر است در Response Header باشد.صفحه اول بطور پیش فرض 1 است یا صفر؟ تعداد رکوردهای یک صفحه بطور پیش فرض چند تاست؟ صفحه اول را شماره 1 مینامیم و پیشنهاد میشود هر صفحه بین 30 تا 100 رکورد داشته باشد.نامگذاری پیش فرض برای پراپرتی های مربوط به pagination چیست؟ page/per_page یا offset/limitهر دو نامگذاری رایج هستند. اگر روش offset/limit را استفاده میکنید مقدار offset برای صفحه اول از صفر باید شروع شود.ویژگی های مدل های APIنامگذاری خوب (خوش-نام) : متاثر از خود مدل ها و URI باید باشد. - از نام قابل درک ریسورس برای کلاینت استفاده شود و نه نامگذاریهای داخلی.- از نامگذاریهایی که مرتبط با دیتابیس یا معماری داخلی است جلوگیری شود.- از پسوند DTO استفاده نکنید. استفاده از DTO در C# برای ارتباطات داخلی مانعی ندارد اما این مدل را به سمت کلاینت نفرستید! - مدل هایی که مستقیما معادل ریسورس ها نیستند میتوانند از پیشوند یا پسوند مناسب مثل Request,Command,Response,Result استفاده کنند. - به نحوه استفاده از نام ها دقت کنید چون هم در کد و هم در سوئگر استفاده میشوند. مثلا مدل مربوط به ایجاد یک Author میتواند CreateAuthorCommand باشد اما در سوئگر یک بدنه جیسون وجود دارد حاوی اطلاعات این مدل است و در حقیقت کلاینت از این نام اطلاعی ندارد.با معیار درست (خوش-معیار) : منسجم و مستقل(loosly coupled)- چندین کانسپت را با هم در یک مدل قرار ندهید. مفاهیم بزرگ را به شکل ریسورس های جدا از هم طراحی کنید.- آبجکت ها را بر اساس مقادیر (primitive value) های مرتبط بسازید. مثلا آبجکت line که بر اساس چهار نقطه به شکل چهار پراپرتی StartX و StartY و EndX و EndY تعریف میشود یا میتوان نقطه شروع را یک پراپرتی و نقطه پایان را یک پراپرتی دیگر قرارداد که هر کدام یک آبجکت از تایپ Point هستند.- وقتی ریسورسهای بزرگ را برای مدلسازی میشکنید از ارتباط یک به یک پرهیز کنید. راه های زیاد و بهتری برای شکستن ریسورس ها وجود دارد. یکی از استثنائات برای این نکته Summary  و Detail است که به شکل Uri resource مانعی برای استفاده ندارد.با اندازه درست (خوش-اندازه) : میزان اطلاعات درون آنها اندازه و درست باشد.- از مدل های بسیار کوچک پرهیز کنید.- از مدل های خیلی بزرگ با sub-record های زیاد یا sub-sub-record های زیاد پرهیز کنید.- سایز مدل شما باید مناسب برای استفاده اکثر کلاینت های هدف باشد. مثلا اگر یک کلاینت دارید فقط برای آن کلاینت مدل را بسازید.- از روش summary/details برای نمایش دیتیل های رکوردها استفاده کنید (استفاده از URI)- از ابزاری برای paging و filtering استفاده کنید.پایداری : باید پایدار باشد و تغییرات روی آن به ندرت اتفاق بیافتد. - اگر چیزی واقعا بعدها نیاز شد به آن اضافه شود و حذف و تغییر روی آن اتفاق نیفتد.- از ورژنینگ برای تغییرات مدل استفاده کنید.آنتی پترن ها در Api Modelاستفاده از نام نا مناسب و غیر استاندارد و غیر قابل درک برای Client.مشکل بعدی شباهت مدل ها به جداول دیتابیس است که در تیم هایی که حول محور داده برنامه نویسی میکنند بیشتر اتفاق می افتد. حواستان باشد که کلاینت قرار نیست نسبت به نحوه ذخیره سازی ریسورسها مطلع باشد!مشکل بعدی تغییر مدلها حین توسعه یا در آینده بدون استفاده از سازوکار ورژنینگ است.مشکل بعدی عدم قابلیت برای کنترل سایز خروجی مدل های api است. مثلا جوری که بنویسید کلاینت نتواند روی تعداد رکوردهای خروجی یک لیست اختیار داشته باشد!مشکل بعدی عدم تهیه document برای client است. مثلا عدم استفاده از swagger میتواند کلاینت را دچار مشکل کند. داکیومنت باید واضح، کامل و به روز باشد.میخواهیم یک مدل بد برای خروجی را که وردپرس استفاده میکند بررسی کنیم.مدل بدنام ID به شکل حروف بزرگ است. جیسون معمولا به شکل camel case است. یعنی با حروف کوچک شروع میشود. پراپرتی های دیگر هم با اینکه حروف کوچک هستند ولی camel case نیستند. همه اسم ها انگار پیشوند دارند به جز Id که هماهنگی وجود ندارد.هیچ نوع ریلیشنی برای post_author تعریف نشده و صرفا به عدد بسنده شده. همچنین برای comment هم ریلیشنی تعریف نشده.مدل بهترمدل فوق اصلاح شده ی مدل قبلی است. نامگذاری camel case شده. آدرس برای author و comment به منظور نمایش ریلیشن تعریف شده است.(استاندارد لینک به ریسورس)مدل api به منظور درک بهتر کلاینت برای کار با api تهیه میشود و نباید دیدگاه دیتابیسی یا دیدگاه معماری نرم افزاری در آن دخیل باشد.جمع بندی : در این مقاله مبانی استفاده از مدل های api بررسی شد. به قانون Postel پرداختیم. در مورد اینکه web api چه مدل دیتایی باید برگرداند پرداختیم. مشخصه های api خوب بررسی شد و آنتی پترن های یک api model را دیدیم.</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Mon, 01 Aug 2022 22:00:01 +0430</pubDate>
            </item>
                    <item>
                <title>معرفی Best Practice ها در Asp.Net Core : پیاده سازی Response</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-asp-core-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-response-web-api-reimy1qvcuyt</link>
                <description> اگر با متدهای HTTP آشنایی ندارید ابتدا این مقاله را مطالعه کنید.این مجموعه با نگاهی به ویدیوی آموزشی Best Practice در Pluralsight که توسط Steve Smith تهیه شده، گردآوری شده است.این مجموعه مقالات به شکل ویدیویی هم در کانال تلگرامم قرار میگیره. خوشحال میشم برای با خبر شدن از مقالات و نوشته ها و ویدیوهای آموزشی در این کانال حضور داشته باشید و نظراتتون رو بنویسید.https://t.me/mediapub_channelفهرست مقالات مجموعه معرفی Best Practice های دات نت :مقدمه : آشنایی با REST و پیاده سازی GET/POST/PUT/DELETEبخش اول : پیاده سازی Response (همین مقاله)بخش دوم : مدل های apiبخش سوم : پیاده سازی apiبخش چهارم : امنیتبه کمک Asp.Net Core میتوان سه مدل خروجی برای Action ها داشته باشیم :1. Specific Types - Primitive types(int,string,...)- Complex types(Author,Customre,Order,...)- Collection (List&lt;T&gt;,Dictionary&lt;T&gt;)- IEnumerable&lt;T&gt;,IAsyncEnumerable&lt;T&gt;2. IActionResult3. ActionResult&lt;T&gt;حالت اول Specefic Typesحالت اول ساده است و به راحتی و بدون اضافه کاری توسط Swagger شناخته و نوع خروجی نمایش داده میشود. مشکل این روش مشکل در ساختن خروجی با تایپ دیگر است. نکته در مورد IAsyncEnumerable : در این روش بدون عملیات buffering به محض دریافت یک رکورد از یک ریسورس (مثل سرویس یا دیتابیس) آن را به سمت ریسپانس هدایت میکنیم و در حقیقت جریان اطلاعات داریم.اولا خود اکشن به شکل async باید باشد و خروجی آن یک به شکل individual Instance و به روش yield باشد. خروجی ما در این حالت آبجکت پروداکت به شکل one by one است، یعنی به محض رسیدن اطلاعات آن از سمت یک دیتاسورس، آبجکت را به بیرون انتقال میدهد. یکی از مثال هایی که با این روش قابل پیاده سازی است فیلترینگ in-memory است. بطور نرمال بهتر است فیلترینگ در نزدیکی دیتاسورس انجام شود اما بعضی اوقات مجبوریم آن را در برنامه خودمان انجام دهیم و این روش راهی را فراهم میکند که بدون buffering کردن نتیجه در یک کالکشن پیش از ریترن،  بتوانیم دیتا را آناً به بیرون پاس دهیم. این موضوع برای پرفورمنس اهمیت دارد و اینکه کمترین فشار به حافظه آورده شود.حالت دوم IActionResultانعطاف پذیر است و زمانی که از BaseController ارث بری میکنید متدهایش مثل OK و NotFound و BadRequest قابل دسترس است. حالت استاندارد شده خروجی و یک پاسخ یکپارچه برای همه متدها از ویژگی های مثبت این روش است. اینکه این نوع پاسخ به شکل Strongly Type نیست میتواند هم نکته مثبت و هم منفی باشد. نکته مثبت به دلیل اینکه مجبور نیستیم تایپ خاصی را هر بار به آن پاس دهیم و نکته منفی به دلیل اینکه ابزارهایی مثل Swagger نمیتوانند تایپ خروجی را حدس بزنند و در صفحه نشان دهند.در کد فوق میبینید که چون خروجی از نوع IActionResult است مجبوریم از اتریبیوت ProduceResponseType استفاده کنیم تا تایپ خروجی برای Swagger قابل نمایش باشد.حالت سوم ActionResult&lt;T&gt;Iاین حالت شبیه حالت دوم است با این تفاوت که Strongly Type است. فواید این روش مجموعه ای از فواید دو حالت اول است ولی هنوز هم در این روش شما برای استفاده سوئگر نیاز به اتریبیوت  ProducesResponseType دارید.همانطور که میبینید برای اتریبیوت ProducesResponseType کافیست کد را معرفی کنید و تایپ خروجی نیازی نیست.نکته : رفتار ریسپانس ها در Minimal Api متفاوت است. سه نوع خروجی IResult (معادل actionResult) و Object و Json  دارند.مثال زیر را ببینید: https://gist.github.com/51025816983b3629e65d69bc2a147b6b هر endpoint یک نام بر اساس نوع ریترن دارد. مثلا getObject یک آبجکت بر میگرداند.سوئگر به شکل زیر است.نوع ریزالت مشخص نیست.نوع ریزالت مشخص است اگر از اکشن ریزالت جنریک استفاده کنیم. برخلاف حالت قبل.با اجرای getObject به شکل جیسون خروجی را خواهیم دید و کانتنت تایپ جیسون است.با اجرای متد getObjectNull در خروجی چیزی نداریم و استتوس 204 یعنی No Content میگیریم. در این حالت کانتنت تایپ مشخص نشده است. بعضی از کلاینت ها انتظار دارند همیشه در خروجی جیسون داشته باشند. در حالتی که getObjectNull را تعریف کردیم این انتظار برآورده نمیشود.در متد getString با اینکه خروجی سریالایز شده یک آبجکت است ولی کانتنت تایپ Text نشان داده میشود. یعنی اگر استرینگ را به بیرون بفرستید کانتنت تایپ Text میشود.در حالت getJson خروجی همان چیزیست که انتظار داریم و کانتنت تایپ آن نیز جیسون است.در حالت ActionResult هم خروجی با استتوس کد و کانتنت تایپ منطبق است ولی سوئگر تایپ خروجی را نمیداند و از قبل نمایش نمیدهد.در حالت IActionResult هم خروجی با استتوس کد و کانتنت تایپ منطبق است و تفاوتش با حالت قبل این است که سوئگر تایپ خروجی را میداند و قبل از اجرای متد نمایش میدهد.در تمپلیت minimal api برای شلوغ نشدن program.cs میتوانیم از کتابخانه های جانبی استفاده کنیم. یکی از آنها  MinimalApi.Endpoint است. که میتوان با آن endpoint ها را به کلاس های جدا منتقل کرد.مثلا برای Create کردن به شکل فوق باید کلاسی از IEndpoint ارث بری کرد. دو متد AddRoute و HandleAsync را باید پیاده سازی کرد. خروجی ما Task از تایپ IResult است و برای minimal Api پیشنهاد میشود. در خروجی از Results.Create استفاده میشد که در آرگومان اولی به آدرسی اشاره میکنیم که ریسورس ساخته شده از طریق آن قابل دسترسی است و در پارامتر دوم آنچیزی که در بدنه قرار است نمایش داده شود (یعنی ریسورس جدید که الان ساخته شده) قرار داده میشود.میخواهیم خروجی این api را با ورودی های فوق بررسی کنیم.آبجکت ساخته شده در body نمایش داده شده و همچنین در هدر ریسپانس ما برای کلید location ،آدرسی معادل مسیر دسترسی به این ریسورس را میبینیم.جمع بندی : - با مطالعه این نوشته و مقاله قبلی باید بتوانیم مفهوم Rest و HyperMedia را توضیح دهیم.- باید با مفهوم Resource ها آشنا شده باشیم که هر چیزیست که یک کد شناسنده یا معرف دارد و میتوانیم api های خود را با توجه به همین مفهوم سازمان دهیم و ایجاد و خواندن و حذف و تغییر ریسورس ها را به عهده بگیریم.- با متد ها یا verb ها آشنا شدیم. اینکه هر کدام در چه حالتی استفاده میشود و امنیت آن چگونه است.- با Http Status Code ها آشنا شدیم. اینکه کدامیک بیشتر استفاده میشوند.- با تایپ های Response در Asp.net Core آشنا شدیم. </description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Mon, 01 Aug 2022 07:40:42 +0430</pubDate>
            </item>
                    <item>
                <title>ولاگ در مورد دوره های آنلاین برنامه نویسی</title>
                <link>https://virgool.io/@mortezadalil/%D9%88%D9%84%D8%A7%DA%AF-%D8%AF%D8%B1-%D9%85%D9%88%D8%B1%D8%AF-%D8%AF%D9%88%D8%B1%D9%87-%D9%87%D8%A7%DB%8C-%D8%A2%D9%86%D9%84%D8%A7%DB%8C%D9%86-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-ky9yy1aierjb</link>
                <description>فردا اولین جلسه از دوره های آنلاین برنامه نویسیم برگزار میشه.یکیش برنامه نویسی مقدماتی(15 ساعت) و دیگری asp.net core (سی ساعت) هست. محتوای فوق العاده حجیمی برای هر دو دوره تهیه کردم. قصد دارم تمرکز بیشتری روی تولید محتوا داشته باشم به همین دلیل کانال تلگرام مرتبط با سایتم رو فعال تر کردم. اگر مطالب ویرگول و ویدیوهای من رو دنبال کرده باشید و به دردتون خورده باشه،حتما از محتوای کانال تلگرام که مختص برنامه نویسی هست استفاده خواهید کرد.ویدیوهای یوتیوب و مقالات ویرگول توی این کانال قرار داده میشه و قابلیت کامنت هم فعاله و میتونیم ارتباط نزدیکتر و سریعتری داشته باشیم. قصد دارم برای سمینارها و جلسات کارگاهی برنامه ریزی کنم تا با هر نوع سلیقه ای ارتباط برقرار کنم و بتونیم با هم توی حوزه برنامه نویسی رشد کنیم.دو سه روز قبل در مورد برگزاری دوره های آنلاین یک ولاگ تهیه کردم که میتونید از طریق لینک یوتیوب که در ادامه آوردمش (اگر نمیبینید یعنی پراکسی ست نکردید) ببینیدش. https://youtu.be/QslyUWCkLJE خلاصه اینکه منتظر ویدیوها و دوره های جدید تر باشید.</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Sat, 30 Jul 2022 20:34:10 +0430</pubDate>
            </item>
                    <item>
                <title>آشنایی با REST و پیاده سازی GET/POST/PUT/DELETE</title>
                <link>https://virgool.io/@mortezadalil/rest-api-tutorial-%D8%A2%D9%85%D9%88%D8%B2%D8%B4-http-atsqhwfkfphs</link>
                <description>قبل از هر چیز بهتر است بدانیم که Best Practice چیست؟معمولا به روش ها و الگوهای پایه و پرکاربرد که توسط تجربه یا تحقیق نتایج بهینه داده اند، Best Practice میگویند. این روش ها را میتوان به منظور استفاده چند باره استاندارد سازی و استفاده کرد.این مقاله مقدمه مجموعه ایست که طی آن قصد دارم Best Practice ها در دات نت کور را بررسی کنیم. نام این مجموعه «معرفی Best Practice ها در Asp.Net Core» است.این مجموعه با نگاهی به ویدیوی آموزشی Best Practice در Pluralsight که توسط Steve Smith تهیه شده، گردآوری شده است.این مجموعه مقالات به شکل ویدیوی نیز در کانال تلگرامم قرار میگیره. خوشحال میشم برای با خبر شدن از مقالات و نوشته ها و ویدیوهای آموزشی در این کانال حضور داشته باشید و نظراتتون رو بنویسید.https://t.me/mediapub_channelفهرست مقالات مجموعه معرفی Best Practice های دات نت :مقدمه : آشنایی با REST و پیاده سازی GET/POST/PUT/DELETE (همین مقاله)بخش اول : پیاده سازی Responseبخش دوم : مدل های apiبخش سوم : پیاده سازی apiبخش چهارم : امنیتفواید استفاده از Best Practice ها چیست؟جلوگیری از گیج شدن : به کمک این روش ها ما روش های عجیب، تکنیک های کاستوم و راه حل های یک دفعه ای را کاهش میدهیم. ما از روش های معروف و آزموده شده استفاده خواهیم کرد. api ما با این روش کاربرد و نگهداری ساده تر خواهد داشت.بهره وری بیشتر : با نوشتن کدهای کمتر و اعمال اقدامات های با ثبات تر. به حداقل رساندن رویه های کاستوم در هر api یعنی تغییرات آینده بسیار سریعتر و آسانتر قابل پیاده سازی خواهند بود.باگ های کمتر در کد : رعایت موارد فوق باعث جلوگیری از ایجاد باگ نیز خواهد شد. الگوی یکسان برای api ها و عدم انجام عملیات تکراری، منجر به رفتار از پیش تعریف شده و ساده تر از سمت کلاینت می شود. کیفیت نرم افزار با توجه به این قواعد بالاتر خواهد رفت.کاهش مشکلات امنیتی : استفاده از Best Practice ها به لحاظ امنیتی نیز به کد ما کمک میکند. چون این روش ها از قبل تهیه و آزمایش شده اند. انجام محافظت های استاندارد در ورودی های سمت کلاینت و کنترل رفتار کلاینت مانع نفوذ به سیستم خواهد شد.درباره Rest and Resources«روی فیلدینگ» پایان نامه دکتری خود را در مورد شیوه معماری نرم افزارهای بر پایه شبکه در سال 2000 ارائه داد. (در دانشگاه UC Irvine) نام این عملیات را Rest گذاشت. بر این اساس میخواهیم روی روش کد نویسی با این شیوه صحبت کنیم.اول باید بدانیم Rest دقیقا چه چیزی است و چه چیزی نیست.یک شیوه معماری برای ایجاد سیستم های توزیع شده بر پایه hypermedia  و designed around resource است. مفهوم HyperMedia یعنی آمیزه ای از صدا و تصویر و متن و لینک ها. این اطلاعات non-linear هستند به این مفهوم که اطلاعات توسط تعدادی لینک قابل بررسی و وارسی کردن هستند. هر صوتی یک رسانه linear محسوب میشود، هر کتاب عادی به دلیل روند سنتی آن یک رسانه linear است. اما کتابی را تصور کنید که شما را مجبور به جابجا شدن بین صفحات میکند. شما تصمیم میگیرید تحت رفتاری non-linear بین صفحات جابجا شوید.معماری Rest بر این فرضیه استوار است که HyperMedia در کنار data به منظور تسهیل کردن تعامل با سیستم ایجاد شده است. نیازی نیست کلاینت در لحظه همه داده های موجود را در اختیار داشته باشد. وجود لینک های مرتبط در Response همین hypermedia است. ممکن است گیج شده باشید. به عنوان مثال یک api برای ثبت اطلاعات مشتری تصور کنید که در ریسپانس علاوه برای شناسه مشتری، یک آدرس به منظور دسترسی به اطلاعات این مشتری برگرداند. اما تحت چه فرمتی؟استاندارد واحدی برای نحوه تولید HyperMedia در پاسخ های Rest Api وجود ندارد. فرمت های متفاوتی وجود دارد. نمونه های از این روش ها در ادامه آورده شده :HAL (Hypertext Application Language)JSON LD (JSON for Linking Data)Collection+JSONSirenJSON Hyper Schemaهر نوع آبجکت، دیتا یا سرویسی که توسط کلاینت قابل دسترسی باشد Resource می نامند. ریسورس ها یک identifier یا مشخص کننده دارند که یک URI معرف آن است. (شاید پیش از این معرف هر ریسورس را شناسه آن در نظر میگرفتید و آن را به نام Id میشناختید، URI معمولا بر اساس همین Id ساخته میشود)طراحی URI برای ریسورس ها باید تحت اصول زیر باشد : از اسم در URI استفاده کنید و نه فعل.از اسم های جمع استفاده کنید و نه مفرد.برای مشخص کردن یک آیتم از ریسورس، از identifier استفاده کنید.نکته 1 : در تهیه api اصل سادگی را دنبال کنید و از غافلگیر کردن کسی که از api شما برای client استفاده میکند پرهیز کنید.نکته 2 : از نمایش آنچه غیر لازم است و detail داخلی محسوب میشود پرهیز کنید. ریسورس های api شما نباید هم نام با اسکیمای دیتابیس یا فرمت داکیومنت های ذخیره شده و یا Entity های شما باشند. باید جزئیات غیرلازم به عنوان ریسورس را کپسوله کنید.نکته 3 : Rest تماما در مورد State است و نه Behavior. این جمله کلیدی برای درک مفهوم Rest است. همینکه همه چیز در api های شما باید با کلمات noun(اسم) نامگذاری شود و نه verb(فعل) همین موضوع را نشان میدهد.نکته 4 : به متدهای HTTP که درخواست ها تحت آن ارسال میشوند verb یا متد میگویند.انواع VERB درخواست GET : مرورگر ها برای نمایش یک صفحه وب از این نوع ریکوئست استفاده میکنند. معمولا additional resource ها هم از نوع GET هستند. درخواست های GET باید Read Only و قابل کش شدن و بدون هیچ گونه ساید افکتی باشند.درخواست POST : مرورگر ها برای سابمیت فرم معمولا از روش POST استفاده میکنند. سرور با این نوع درخواست یک resource جدید با اطلاعات فراهم شده می سازد.درخواست PUT : برای آپدیت ریسورس ها استفاده میشود. در این حالت انتظار می رود آنچه به عنوان اطلاعات جدید برای آپدیت ریسورس ارسال میشود کل اطلاعات آن ریسورس باشد.(فقط بخشی که آپدیت میشود نباشد).درخواست DELETE : برای حذف ریسورس از این نوع درخواست استفاده میشود.درخواست PATCH : برای آپدیت ریسورس به شکلی که فقط بخشی از آن ارسال و همان بخش فقط آپدیت شود این نوع درخواست استفاده میشود.باید از استفاده غیراستاندارد این درخواست ها پرهیز کرد. اگر از الگوی استاندارد استفاده کنید، API های شما قابل فهم تر برای دیگران و حتا برای ابزارهای توسعه خواهد بود.ملحقات VERB متد HEAD : بخشی از دیتاها که برای افزایش پرفورمنس و جلوگیری از ارسال همه ی ریسورس های وابسته می توان از این بخش استفاده کرد. در پاسخ این ریکوئست بدنه حاوی اطلاعات وجود ندارد.متد OPTIONS : یک روش سبک و ساده برای دریافت اطلاعات سرور در مورد نوع ریکوئست هایی که سرور پشتیبانی میکند.این جدول ملاحظاتی را که برای انتخاب مناسب متدها برای انجام یک عملیات باید انجام شود نشان میدهد. ستون idempotent یعنی آیا این متد با تکرار خود روی وضعیت ریسورسها بی اثر است؟ مثلا متد GET یا DELETE یک آیتم اگر بارها هم تکرار شوند تغییر جدیدی روی ریسورسها اعمال نمیکنند (آیتم همان بار اول حذف میشود و تکرار این حذف اثر خاصی ندارد). مثلا متد POST با هر بار صدا زده شدن، باید باعث تغییر ریسورسها شود و اطلاعات را دوباره ثبت کند.ستون Safe یعنی آیا State ریسورس ها با هر متد دستخوش تغییر میشود یا نه. درصورتی یک متد Safe است که State ریسورسها را تغییر ندهد.نکاتی که باید برای متدهای POST و PUT رعایت کردبرای اضافه کردن یک ریسورس جدید از متد POST و برای جایگزینی یک ریسورس موجود با یک ریسورس جدید از متد PUT استفاده میشود.متد POST معمولا روی یک مجموعه ریسورس اعمال میشود مثلا روی authors/ ، ولی متد PUT معمولا روی یک تک آیتم ریسورس اعمال میشود مثل 1/authors/ .متد POST حاوی شناسه یا Id نیست و خود سرور مسئول تولید شناسه برای این ریسورس است ولی متد PUT باید مشخص کند که روی چه شناسه ای قرار است تغییرات اعمال کند. این شناسه در خود آدرس یا بدنه درخواست باید قید شود. دو حالت برای برخورد با شناسه در آدرس و بدنه وجود دارد : حالت اول : اگر سیستم ما اجازه تغییر Id را بدهد باید Id بدونه جایگزین Id قبلی شود، پس هنگام ارسال درخواست PUT شناسه ای که در آدرس ارسال میشود با شناسه موجود در بدنه متفاوت است.حالت دوم : زمانیست که سیستم ما اجازه تغییر Id را ندارد. طبق استاندارد باید Id موجود در آدرس را با Id بدنه مقایسه کرد، اگر برابر نبودند خطای Bad Request صادر شود.در تصویر فوق نمونه CRUD برای Author را میبینید که از کالکشن POSTMAN عکس گرفته شده است.تصویر کامل Postman بعد از درخواست GET و نحوه مقداردهی به ورودی و کد خروجی 200تصویر کامل Postman بعد از درخواست POST و نحوه مقداردهی به ورودی و کد خروجی 201تصویر کامل Postman بعد از درخواست POST و وجود آدرس منطبق با ریسورس جدید در اتریبیوت لوکیشن موجود در هدر خروجی (Response Header)تصویر کامل Postman بعد از درخواست DELETE و نحوه مقداردهی به ورودی و کد خروجی 204 بدون بدنه خروجیتصویر کامل Postman بعد از درخواست GET برای نمایش لیست و کد خروجی 200 تصویر کامل Postman بعد از درخواست PUT برای به روز رسانی یک ریسورس و کد خروجی 200 . بدنه و آدرس هر دو مقدار شناسه را دارند.تصویر کامل Postman بعد از درخواست PATCH برای به روز رسانی بخشی از یک ریسورس و کد خروجی 200. بدنه به شکل قراردادی به شکلیست که نشان میدهد چه اتفاقی (replace) برای کدام پراپرتی (twitterAlias) و با چه مقدار جدیدی (jlerman) بیفتد.نکاتی در مورد Status Codesکدهای دریافت شده در Response ها در پنج دسته یا کلاس جای میگیرند.100-199 با هدف اطلاع رسانی و به ندرت استفاده میشوند.(Informational)200-299 به منظور نمایش موفقیت در انجام عملیات خواسته شده استفاده میشوند.(Successful)300-399 به منظور نمایش اینکه Resource در جای دیگری باید یافت شود استفاده میشند.(Redirection)400-499 به منظور نمایش خطاهای مختلف مرتبط با کلاینت مثل Authentication یا Authorization یا پیدا نشدن ریسورس استفاده میشوند.(Client Error)500-599 به منظور نمایش خطاهای سمت سرور استفاده میشوند.(Server Error)نکته : کلاینت باید رفتار با این کدها را مشخص کند. رفتار با کدهای نامشخص باید بر اساس رقم اول صورت گیرد.(یعنی x00)عملیات مختلف دارای status code های مختلف و مناسب خودشان در response هستند.در ریکوئست های GET برای دریافت یک ریسورس :پس از دریافت اطلاعات استتوس کد 200 یعنی موفق بوده است.اگر ریسورس خواسته شده منتقل شده باشد کد 301 یا 302 برگردانده میشود.اگر از Cache استفاده شده باشد، کد 304 به معنی Not Modified برگردانده میشود یعنی از همان کپی ریسورس قبلی برای این ریکوئست استفاده شده است.اگر ریسورس درخواست شده موجود نباشد خطای 404 برگردانده میشود.اگر در یک محدوده زمانی مشخص تعداد زیادی ریکوئست به سمت سرور ارسال شود خطای 429 به معنی تعداد ریکوئست زیاد صادر میشود.در ریکوئست های POST برای ایجاد یک ریسورس : اگر درخواست POST برای گرفتن Access Token بوده باشد و هیچ ریسورسی تغییر نکرده یا ساخته نشده باشد کد 200 برگردانده میشود.اگر درخواست POST با ایجاد یک ریسورس جدید در سمت سرور و ایجاد یک لینک به عنوان URI برای دسترسی به آن ریسورس و قرار دادن آن در هدر با عنوان location صورت گیرد، در این حالت کد 201 برگردانده میشود.اگر درخواست POST به شکل یک پردازش Async باشد یعنی ریکوئست به سرور رسیده ولی پراسس همان لحظه تکمیل نشده و به پایان نرسیده باشد( ریسپانس میتواند هدری حاوی URI به عنوان endpoint داشته باشد) کد 202 برگردانده میشود.اگر درخواست POST هیچ نوع URIی در هدر ریسپانس به عنوان location نداشته باشد، کد 204 برگردانده میشود.(به ندرت چنین چیزی ممکن است)اگر درخواست POST مانند ایجاد یک ریسورس جدید باشد اما برای caching طراحی شده باشد از کد 303 استفاده میشود.اگر درخواست POST دارای فرمت ورودی غیرعادی باشد یا عملیات با خطا مواجه شود کد 400 برگردانده میشود.اگر در یک محدوده زمانی مشخص تعداد زیادی ریکوئست به سمت سرور ارسال شود خطای 429 به معنی تعداد ریکوئست زیاد صادر میشود.در ریکوئست های PUT یا PATCH برای تغییر یک ریسورس :اگر درخواست PUT یا PATCH بدون body یا با body در ورودی باشد در هر صورت در حالت موفقیت کد 200 برگردانده میشود.اگر تمایلی به برگرداندن body در خروجی ندارید در حالت موفقیت کد 204 مناسب تر است.اگر ریسورسی ساخته شد که یک URI قابلیت اشاره به آن را داشت کد 201 مناسب است.اگر ریکوئست منتج به یک پردازش طولانی (longer running process) شد و همان لحظه کامل نشد یا حتا شروع نشده باشد. (پاسخ میتواند حاوی یک URI باشد که کلاینت به کمک آن بتواند وضعیت را چک کند). در این حالت از کد 202 استفاده میشود.اگر درخواست PUT یا PATCH هیچ بدنه یا هیچ نوع URIی در هدر ریسپانس به عنوان location نداشته باشد، کد 204 برگردانده میشود.اگر درخواست PUT یا PATCH دارای فرمت ورودی غیرعادی باشد یا عملیات با خطا مواجه شود کد 400 برگردانده میشود.اگر آدرس درخواست متناظر یا هیچ آدرس از پیش تعریف شده برای دسترسی به ریسورس ها نباشد خطای 404 برگردانده میشود.اگر کلاینت نتواند یک ریسورس را تغییر دهد به دلیل اینکه نسبت به آخرین دریافت کلاینت، ریسورس تغییر کرده است در این حالت کد 409 برگردانده میشود. اطلاعات payload باید کلاینت را برای رفع این مشکل راهنمایی کند.اگر در یک محدوده زمانی مشخص تعداد زیادی ریکوئست به سمت سرور ارسال شود خطای 429 به معنی تعداد ریکوئست زیاد صادر میشود.در ریکوئست های DELETE برای حذف یک ریسورس :اگر موفقیت آمیز باشد 200 برگردانده میشود.اگر درخواست به شکل async و نیازمند به پردازش بلند مدت باشد و همان لحظه کامل نشود از کد 202 استفاده میشود.اگر موفقیت آمیز باشد ولی چیزی برگردانده نشود از کد 204 استفاده میشود.(حالت رایج)اگر آیتم در ریسورس پیدا نشود یا قبلا حذف شده باشد از کد 404 استفاده میشود.اگر در یک محدوده زمانی مشخص تعداد زیادی ریکوئست به سمت سرور ارسال شود خطای 429 به معنی تعداد ریکوئست زیاد صادر میشود.در ریکوئست های مرتبط با Security و Permission :رایج ترین پاسخ در این نوع ریکوئست ها در زمانی که مجوز دسترسی وجود نداشته باشد 401 است.اگر کلاینت اعتبار سنجی شود، یعنی از سمت سرور شناخته شده باشد اما مجوز دسترسی به ریسورس خواسته شده را نداشته باشد از کد 403 استفاده میشود.اگر ریسورس پیدا نشود یا بَکند نخواهد به دلایل امنیتی وجود ریسورس را برای کلاینت فاش کند از کد 404 استفاده میشود. (مثلا برای دسترسی به یک ریپازیتوری پرایویت از Github شما خطای 404 میگیرید)خطاهای هندل نشده :تنها چیزی که برگردانده میشود خطای 500 است. احتمالا به همراه یک سری رفرنس برای یافتن دلیل خطا در فایل log در سرور.نکته مهم :هرگز خطا را در پاسخ ریکوئست هایی که ریسپانس با کد 200 دارند قرار ندهید یا به عبارت دیگر اگر StatusCode برابر با 2xx باشد یعنی هیچ خطایی رخ نداده است یا به عبارت دیگر اگر خطایی در انجام عملیات روی ریسورس رخ داد آن را با کد 2xx به کلاینت نمایش ندهید. api هایی که با روش غلط پیاده سازی شده باشند برای استفاده در برنامه های دیگر به سختی قابل استفاده هستند چون بررسی دوباره ریسپانس های 2xx به منظور یافتن خطا و مدیریت آن باید انجام شود!مثالی برای نمایش خطا وقتی برای یک ریکوئست از نوع POST یکی از پراپرتی های لازم، خالی ارسال شده است.مثالی برای نمایش خطای 500 که با دستکاری endpoint ایجاد کردیم. در این حالت چون در حالت development هستیم خطا را نمایش میدهیم. با تنظیمات در کد میتوانیم نمایش جزئیات خطا را در حالت production محدود کنیم یا آن را با کد و متن مشخصی معادل کرده و آن را نمایش دهیم.هرگز به کمک try/catch خطای 500 را به 200 تبدیل نکنید، چون استفاده از کدی که اینگونه نوشته شده مشکل خواهد بود و مدیریت خطا از سمت کلاینت غیراستاندارد خواهد شد.نتیجه گیری :برای متدهای CRUD خود اسم جمع انتخاب کنید و هر کدام به طور مستقل اسم جداگانه نداشته باشد و حتما با متدهای HTTP از هم تمیز داده شوند.در خروجی POST حتما در بخش هدر، اتریبیوت Location را با آدرس مرتبط با آنچه به ریسورس ها افزوده اید تکمیل کنید و از یک URI استفاده کنید.در متد های PUT حتما شناسه را در آدرس و هم در بدنه دریافت و مقایسه کنید. این دو شناسه در سیستم هایی که Id قابل تغییر نیست باید برابر باشند. در غیر اینصورت خطای Bad Request تولید کنید.خطاهای خود را با کد 200 به سمت کلاینت نفرستید.</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Wed, 27 Jul 2022 18:13:05 +0430</pubDate>
            </item>
                    <item>
                <title>چطور برنامه نویسی یاد بگیریم؟</title>
                <link>https://virgool.io/@mortezadalil/%DA%86%D8%B7%D9%88%D8%B1-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3%DB%8C-%DB%8C%D8%A7%D8%AF-%D8%A8%DA%AF%DB%8C%D8%B1%DB%8C%D9%85-hhef49oqmmha</link>
                <description>این چند وقت درگیر تهیه سرفصل و محتوای فکر شده برای دوره آنلاین آموزش برنامه نویسی بودم و مطالب زیادی رو هم خوندم و هم دیدم.خیلی از ویدیوهای آموزشی یوتیوب شاید به لحاظ بازدید کم تعداد اما به لحاظ محتوا با کیفیت هستند. یکی از این ویدیوها ویدیویی هست که براساس محتواش تونستم این ویدیو رو بسازم.اگر فکر میکنید زبان جاوااسکریپت زبان مناسبی برای شروع برنامه نویسی هست، من هم با شما موافقم. https://youtu.be/YJB2nGWKSfU  این ویدیو و توضیحاتش به طور کامل توی کانال تلگرامم هم موجوده. خوشحال میشم عضوش بشین و با حضور و نظراتتون به من در تولید محتوای آموزشی کمک کنید.لینک کانال تلگرام :https://t.me/mediapub_channel</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Fri, 01 Jul 2022 13:36:43 +0430</pubDate>
            </item>
                    <item>
                <title>دوره آنلاین Asp.Net Core</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-aspnet-core-kdcl6rikuanj</link>
                <description>برای دیدن ویدیوهای من در مورد برنامه نویسی عضو این کانال شوید :  https://t.me/mediapub_channelبرای تابستون امسال برنامه های ویژه ای دارم. به غیر از تولید ویدیوهای آموزشی برای یوتیوب، مطالعه چندین کتاب دات نتی چنان منو سر ذوق آورد که چند ماهه به طرح ریزی و پختن ایده دوره آموزشی آنلاین فکر می‌کنم. دوره‌هایی که اساسشون «من در آوردی» نیست و روی طرحشون وقت گذاشتم. دوره «آموزش مقدماتی برنامه‌نویسی» و دوره «Asp.Net Core» . (چهار تا دوره بود ولی بعیده به جز این دو تا فرصت کنم بقیه رو برگزار کنم)دوره اول رایگان و دوره دوم هزینه اندکی داره که متناسب با وقتی هست که روی جمع آوری مطالب و طرح ریزی بیان مطالب گذاشتم. کیفیت و بازخورد این دوره برای من خیلی مهمه. قصد دارم ویدیوهایی از این دوره‌ها برای یوتیوب استخراج کنم. سوالات و گره‌های فکری شرکت کننده‌ها ایده اصلی برگزاری این دوره‌هاست. اگر قصد ثبت نام یا سوالی دارید به اکانت تلگرامم mortezadalil پیام بفرستید تا لینک ثبت نام براتون بفرستم.اگر علاقمند به دات نت هستید دوره Asp.Net Core محتوای به دردبخوری داره. سرفصل‌های این دوره رو ببینید(دو بخش ملزومات سی شارپ برای افرادی که با زبان دیگری آشنایی دارند و آشنایی با Sql برای افرادی که از sql چیزی نمیدانند به این سرفصل اضافه شده):مقدماتآشنایی با دات نت کورچرا دات نت کور؟دات نت کور چطور کار می‌کند؟اولین برنامهمروری بر اپلیکیشن های دات نت کورایجاد اولین برنامهاجرای برنامه تحت وبآشنایی با ساختار پروژهآشنایی با Web Hostآشنایی با کلاس Startupتولید محتوا با Razor Pageآشنایی با Middleware Pipelineآشنایی و استفاده از Middlewareمدیریت خطا به کمک Middlewareایجاد وب سایت به کمک Razor Pageمقدمه و معرفیمقایسه Razor Page و MVCآشنایی با Page Handlerانطباق آدرس (route) با Razor Page هاروتینگ چیست؟روتینگ در Asp.Net Coreکاستومایز کردن روتینگآشنایی با Route templateآشنایی با Route Patternانتخاب یک Page Handler و اجراتنظیم شرایط قراردادی Razor Pageمفهوم Model Bindingدرک مدل ها در Razor Pageمدل در ریکوئستمفهوم Model Validationساخت HTML به کمک Razor Viewمفهوم Viewساخت Razor Viewمفهوم صفحات داینامیک با Razorآشنایی با Layout,Partial view و _viewstartانتخاب ویو در MVC Controllerساخت فرم با Tag Helperایجاد فرمایجاد لینکتوانایی های تگ هلپرایجاد Web Api برای موبایل یا هر نوع کلاینت به کمک MVCآشنایی و مقدماتیک پروژه ساده Web Apiاستفاده از MVC در Web Apiلینک کردن Action به Routeاتریبیوت ApiControllerتولید پاسخ به کمک Modelتنظیمات سرویس ها به کمک DIآشنایی با DIاستفاده از Containerدرک چرخه حیات یک سرویستنظیمات اپلیکیشن Asp.Net Coreمقدماتکانفیگ با CreateDefaultBuilderآشنایی با آبجکت Configurationآشنایی با Environmentذخیره سازی اطلاعات با Entity Frameworkآشمایی با Ef Coreاضافه کردن Ef Core به پروژهمایگریشنکوئری گرفتن و ذخیره اطلاعاتآشنایی با Filterهافهم فیلتر و روش استفادهایجاد فیلتر دلخواهتوانایی های فیلترکار با user در پروژه یا Authenticationآشنایی با Authentication و Authorizationآیدنتیتی چیست؟اضافه کردن آیدنتیتی به پروژهمدیریت کاربرانامنیت اپلیکیشن یا Authorizationمفهوم Authorization در Asp.Net Coreاستفاده ار Policyها در حالت claim-basedایجاد policy به شکل کاستوممدیریت سطح دسترسی هانسخه های دات نتتفاوت نسخه های اخیر دات نتنسخه ۷ دات نت چگونه خواهد بود؟انتشار اپلیکیشنمفهوم Hostingانتشار به کمک iisانتشار در لینوکسبهینه سازی هالاگ و عیب یابی برنامهاضافه کردن لاگساختار لاگبهینه سازی امنیت در اپلیکیشناستفاده از Httpsحمله های XSSمحافظت در برابر CSRFساخت Background Taskایجاد بک گراند تسک با IhostedServiceایجاد Worker Service با IHostمعرفی کتابخانه های مفیدجمع بندیچه کار میتوانیم و چه کار نمیتوانیم انجام دهیم؟معماری سنتی سه لایهمعماری Clean architectureمقدمات CQRSمعرفی میکروسرویسچرا باید DDD بدانیم؟</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Thu, 23 Jun 2022 01:15:39 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش میکروسرویس Microservice - پیاده سازی یک میکروسرویس برای محاسبه قیمت (بخش هفتم)</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D9%85%DB%8C%DA%A9%D8%B1%D9%88%D8%B3%D8%B1%D9%88%DB%8C%D8%B3-%D9%88-grpc-%D9%88-%D8%AF%D8%A7%DA%A9%D8%B1-ohyi7bhiv7l5</link>
                <description>بخش اول : آشنایی با مفهوم میکروسرویسبخش دوم : ویژگی های اصلی یک میکروسرویسبخش سوم : تحلیل یک پروژه کوچک بر اساس میکروسرویس هابخش چهارم : شروع پیاده سازی یک پروژه فروشگاهیپیشنیاز 1 بخش پنجم : آموزش Node و Typescript برای تولید apiپیشنیاز 2 بخش پنجم : آموزش داکر و مفاهیم اولیهبخش پنجم : پیاده سازی یک میکروسرویس برای نمایش کالاهاپیشنیاز 1 بخش ششم : آشنایی با Asp.net core 6بخش ششم : پیاده سازی یک میکروسرویس برای کار با سبد خریدبخش هفتم : پیاده سازی یک میکروسرویس برای محاسبه تخفیفات (همین مقاله)بخش هشتم: پیاده سازی یک میکروسرویس برای ثبت سفارشبرای دیدن ویدیوهای من در مورد برنامه نویسی عضو این کانال شوید :  https://t.me/mediapub_channelدر این بخش میخواهیم یک میکروسرویس دیگر به منظور استخراج قیمت کالا و تخفیفات مرتبط با کالا پیاده سازی کنیم.فرض کنید یک جدول به نام Price در این میکروسرویس داریم که شناسه کالا و قیمت کالا را در خود نگه میدارد. همچنین این جدول به ازای هر کالا چندین قیمت در تاریخ های مختلف دارد پس برای استخراج قیمت کالا ما نیاز به آخرین قیمت آن کالا داریم.  میتوانستیم یک جدول برای درج تخفیف به نام Discount تعریف کنیم که هر کالا تخفیف های مختلفی بر اساس کد تخفیف داشته باشد و قابلیت فعال یا غیرفعال بودن را نیز تعریف کنیم. ده‌ها راهکار و روش برای پیاده سازی قیمت و تخفیف بر اساس مناسبت و کاربر و تعداد خرید و ... وجود دارد که طبیعتا در این مجموعه مقالات فرصت پرداختن به روش های قیمت گذاری و تخفیف نیست.میخواهیم فقط یک جدول ساده قیمت به نام Price ایجاد کنیم و به کمک دات نت api های مورد نیاز را بنویسیم. با سناریویی که در این مقاله تعریف شده این میکروسرویس باید به یک میکروسرویس دیگر خدمات ارائه دهد. یعنی به کمک gRPC که به آن میپردازیم، این میکروسرویس صدا زده میشود و قیمت کالا را در اختیار میکروسرویس دیگر قرار میدهد. طبیعتا ایجاد و ویرایش و حذف قیمت برای یک کالا میتواند در همین میکروسرویس و به شکل Rest Api انجام شود و تبدیل api های آن به gRPCبی فایده است چون پنل مدیریت این سایت مشکلی برای فراخوانی مستقیم Http Request ها یا همان api های REST ندارد.پس در نظر داشته باشید این میکروسرویس هم به بیرون ( رابط کاربری پنل مدیریت) و هم به میکروسرویس داخلی (میکروسرویس Basket ) خدمات خواهد داد.انتظار من این است که خواننده بعد از مطالعه این نوشته بتواند موارد زیر را به راحتی تجزیه تحلیل کنید :ایجاد یک پروژه دات نتآشنایی با PostgreSQLآشنایی با PgAdminآشنایی با Dapperایجاد یک CRUD ساده با دات نتآشنایی به Swagger  و استفاده از آن به جای Postmanآشنایی با gRPCآشنایی با فایل      ProtoBufپیاده سازی gRPC داخل یک پروژه web Apiآشنایی با BloomRpcآشنایی با grpCurlروش override کردن محتوای appsettings در فایل تنظیمات docker-composeاستفاده از PostgreSQLپُستگره، یک دیتابیس اوپن سورس و رایگان و ریلیشنال با ویژگی ها و فواید قدرتمند است.به دلیل امنیت ، مقیاس پذیری و اطمینان در ذخیره سازی، از پُستگره به عنوان یک ابزار مدیریت دیتابیس در بسیاری از جاها استفاده میکنند. به دلیل دقت، قدرت و شکل معماری دیتا یکی از سیستم های مدیریت دیتابیس مورد قبول در این روزهاست.این دیتابیس نسبت به mysql و بقیه دیتابیس های هم رده سرعت insert و update کندتری دارد. چون یک دیتابیس Transaction based است.یک تیبل Price خواهیم ساخت که اطلاعات قیمت را در آن نگهداری میکنیم.میخواهیم تنظیمات و نصب Postgre را به کمک داکر انجام دهیم.طبق روال قبل که در مقالات پیشین به آن اشاره شد نسخه ایمیج رسمی این دیتابیس را پیدا کرده و pull میکنیم و روی داکر سیستم شخصی کانتینر آن را میسازیم.برای دریافت این ایمیج دستور زیر را مینویسیمdocker pull postgresبرای اجرای این ایمیج و تبدیل آن به کانتینر از دستور زیر استفاده میکنیم. یوزر را postgres و پسورد را 123456 تعیین میکنیم :حالا چک میکنیم این دیتابیس درست نصب شده باشد و قابل استفاده باشد :حالا به کمک ابزاری که روی Visual Code نصب کردیم مثل مقاله قبلی با این دیتابیس هم ارتباط برقرار میکنیمخب اتصال از این روش با موفقیت برقرار شده است.اگر میخواهید خیلی رسمی تر به این دیتابیس متصل شوید و از محیط GUI مرسوم استفاده کنید، ایمیج PGAdmin را به شکل زیر نصب کنید:docker pull dpage/pgadmin4docker run -p 5050:80 -e &amp;quotPGADMIN_DEFAULT_EMAIL=mortezadalil@gmail.com&amp;quot -e &amp;quotPGADMIN_DEFAULT_PASSWORD=1234567&amp;quot -dدر مرورگر آدرس لوکال هاست با پورت 5050 را مینویسیم تا به محیط مدیریت متصل شویم. یوزرنیم و پسورد pgAdmin را وارد میکنیم و صفحه زیر را خواهیم دید : روی Add new server کلیک میکنیم تا پنجره زیر رویت شود.انتخاب یک نام دلخواه برای کانکشناسم برای این کانکشن به نام pricedb انتخاب میکنیم و به تب بعدی میرویم.عدم دسترسی به localhost:5432با وارد کردن اطلاعات فوق خطایی که میبینید مشاهده میکنیم.واضح است که ما از روی سیستم خودمان و به کمک Visual Code میتوانیم به دیتابیس Postgres متصل شویم ولی از طریق محیط PGAdmin نمیتوانیم چون این محیط در یک کانتینر دیگر قرار دارد و به آدرس localhost:5432 دسترسی ندارد. حتا اگر اسم کانتینر دیتابس هم وارد کنیم به آن دسترسی ندارد چون در یک شبکه نیستند.حتا با وارد کردن اسم کانتینر هم به آن دسترسی نداریم.باید این دو را در یک شبکه قرار دهیم این موضوع را در مقالات قبلی ررسی کردیم. (در این مقاله در بخش ساخت فایل داکر)کانتینرها را پاک میکنیم.اولش پاک نکرد چون باید ابتدا استاپ میکردیم ولی ما از -f برای حذف زورکی کانتینر استفاده کردیم! دستورات را طوری مینویسیم که در یک شبکه واحد باشندdocker network create node-postgres-networkdocker run --name mypostgres --network node-postgres-network -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=123456 -p 5432:5432 -d postgresdocker run -p 5050:80 --network node-postgres-network -e &amp;quotPGADMIN_DEFAULT_EMAIL=mortezadalil@gmail.com&amp;quot -e &amp;quotPGADMIN_DEFAULT_PASSWORD=1234567&amp;quot -d dpage/pgadmin4حالا در مرورگر localhost:5050 را صدا میزنیم و یوزر پسورد PGAdmin را وارد میکنیم مراحل قبل را تکرار میکنیم و اینبار ارتباط برقرار خواهد شد.دقت کنید فعلا قصد develop داریم و اجرای کانتینرهای مرتبط با هم را به شکل دستی با خود داکر هندل کردیم ولی حتما میدانید که میتوانستیم یک فایل docker-compose برای ارتباط این دو کانتینر بنویسیم و این مسیر طولانی را طی نکنیم. در انتها مثل مقالات قبلی به کمک docker-compose پابلیش میکروسرویس ها و دیتابیس ها را هندل خواهیم کرد.حالا دیتابیس مورد نظر را میسازیم:حالا تیبل مورد نظر را میسازیم:نام را در تب جنرال برای تیبل به نام Price انتخاب کردیم و در تب دوم به شکل زیر ستون ها و نوعشان را مشخص میکنیم و در نهایت دکمه Save را میزنیم.میتوانیم به شکل زیر هم تیبل را بسازیم :دو ردیف دیتا برای یک کالای خیالی ایجاد میکنیم:یعنی قبلا قیمت کالا 1000 بوده و بعدا به 1500 تغییر کرده.در بخش کوئری کالای شماره 1 را سرچ کرده و به ترتیب تاریخ نزولی مرتب میکنیم و اولین نتیجه را که همان آخرین تغییر قیمت است بیرون میکشیم:این کوئری را به خاطر داشته باشید که بعدا با آن کار داریم.قبل از ایجاد پروژه یادآوری میکنیم که به کمک اکستنشنی که پیش از این برای ویژوال کد معرفی کردم میتوانید به تیبل ایجاد شده دسترسی داشته باشید:تولید api برای کار با میکروسرویس قیمت و تخفیفات1. مراحل ایجاد پروژه را بر اساس مقاله قبل انجام دهید و یک پروژه به نام Discount.api بسازید. (مراحل 1 تا 4)2. مدلی که قرار است این میکروسرویس با آن کار کند به شکل زیر ایجاد میکنیم. https://gist.github.com/2d488c059c8e3fefad073376c1b0c818 3.  حالا باید تصمیم بگیریم چه api هایی قرار است ایجاد کنیم. یک تیبل ساده ایجاد کردیم که به ازای هر کالا در      تاریخ های مختلف قیمت (Amount) و قیمت بعد از تخفیف(CurrentAmount) را در خود دارد. طبیعتا این حالت ساده در یک پروژه واقعی جوابگوی همه نیازها نیست ولی برای اهداف آموزشی مدل رضایت بخشیست! مثلا ممکن است قیمت کالا برای یک کاربر خاص تخفیف داشته باشد یا بهتر باشد تاریخچه تغییر قیمت را دقیق تر داشته باشیم که این موارد بیزنس متفاوتی میطلبد.آنچه برای این میکروسرویس نیاز داریم به شکل زیر است:متد GetPrices که قیمتهای مربوط به یک کالا را برمیگرداند.متد CreatePriceمتد UpdatePriceمتد DeletePriceدر مقاله قبل هم گفته شد که قرار نیست تمامی قواعد Clean Architecture را رعایت کنیم و این میکروسرویس صرفا جنبه آموزشی به لحاظ قرارگیری در یک مجموعه میکروسرویس ها را دارد. در ادامه این api ها را ایجاد میکنیم.4. میخواهیم از Dapper به عنوان یک ORM درست و حسابی استفاده کنیم. از خوبیهای Dapper همینقدر کافیست که بدانیم در سایت StackOverflow استفاده شده. برای اطلاعات بیشتر این ویدیو را ببینید.Entity Framework Community Standup - Dapperبرای کار با دپر ایجاد ریپازیتوری و متدهای جنریک از پیش تعریف شده به منظور Pagination و Sort و Filter توصیه میشود. متاسفانه در این مقاله مجال پرداختن به این مسائل را نداریم.دپر را به شکل زیر نصب میکنیمdotnet add package Dapperهمچنین برای دسترسی دات نت به PostgreSQL کتابخانه زیر را نصب میکنیمdotnet add package Npgsqlبعد از نصب فایل csproj به شکل زیر خواهد شد:5. حالا کانکشن استرینگ را مشخص میکنیم :6. کنترلری به نام DiscountController میسازیم و شروع به پیاده سازی متدها میکنیم.7. متد GetPrices را طوری مینویسیم که شناسه کالا را بگیرد و کل قیمت های آن را به شکل نزولی بدهد.برای تست به کمک dotnet run در ترمینال، پروژه را اجرا کرده و از Swagger به جای Postman استفاده میکنیم.سوئگر ابزاریست که میتوانیم به کمک آن api های تولید شده به همراه ورودی و خروجی های آن را ببینیم و تست کنیم.به طور پیش فرض این کتابخانه در پروژه webapi دات نت کور وجود دارد.کافیست بعد از اجرا در مرورگر عبارت زیر را بنویسیم :https://localhost:7256/swagger/index.html8. متد مربوط به ایجاد و آپدیت و حذف رکورد قیمت را مینویسیم تا کنترلر کامل شود. https://gist.github.com/201c10701bb23961d9c0560bc295d88c درباره gRPCآنچه تا اینجا دیدیم تقریبا چیزی شبیه به مقاله قبل یعنی CRUD مربوط به سبد خرید بود. در حقیقت تنها دیتابیس و ORM ما متفاوت بود. اما به شکل ارتباط بین میکروسرویس ها در ابتدای مقاله دقت کنید. میکروسرویس Basket وظیفه دارد اطلاعات قیمتی مربوط به کالاهای موجود در سبد را از میکروسرویس Discount استخراج کند. این ارتباط داخلی و بین دو میکروسرویس است. با چیزی که تا الان نوشتیم متد GetPrices از بیرون قابل استفاده است و با یک Http Request چه از طرف مرورگر و چه از طرف یک ماژول دیگر میتواند پاسخ مناسب را برگرداند، اما برای ارتباط داخلی تا اندازه ای کند است یا بهتر بگوییم تکنولوژی دیگری وجود دارد که با سرعت بهتری پاسخ را برمیگرداند.از تکنولوژی grpc زمانی که نیاز به پاسخ سریع برای ادامه عملیات باشد برای ارتباط بین میکروسرویس با میکروسرویس دیگر در Back-end استفاده میشود.جی آر پی سی قابلیت پشتیبانی از برنامه نویسی mixed دارد مثلا یک طرف python و دیگری سی شارپ باشد.توان عملیاتی بالا و پاسخ سریع از ویژگیهای ارتباط gRPC است.میتوان برای ارتباط point to point از grpc استفاده کرد تا مسیج ها را به شکل Realtime ارسال نمود.حجم کمتر اطلاعات ارسالی به نسبت json در این روش از مزیت های مهم gRPC است.به کمک gRPC میتوان الگوهای محاسباتی را بین چندین میکروسرویس پیاده سازی کرد و در نهایت خروجی را به سمت کلاینت ارسال نمود.تنظیمات مربوط به gRPC باید در دو سوی آن انجام شود. یعنی هم میکروسرویس Basket به عنوان مصرف کننده (Consumer or Client) و هم میکروسرویس Discount به عنوان خدمات دهنده (Server) کتابخانه های مربوط و تنظیمات مرتبط را داشته باشند.برای برقراری ارتباط gRPC نیاز به استفاده از پروتکل http/2 است.ما در ادامه بخش Server یک ارتباط gRPC را پیاده خواهیم کرد. تا اینجای کار یک CRUD ساده به کمک Rest API را پیاده سازی کرده ایم. به دلایلی که گفته شد میخواهیم متد GetPrices را به کمک gRPC هم پیاده سازی کنیم.پیاده سازی بخش Server یک ارتباط gRPC1. اگر بخواهیم داخل پروژه Api خود سرویس هایی به عنوان Server وبا ارتباط gRPC بنویسیم، باید ابتدا باید دستور زیر را برای اضافه شدن کتابخانه مورد نیاز استفاده کنید(ممکن است در ارتباط برقرار کردن با آدرس های گوگل به مشکل بخورید پس حتما از VPN استفاده کنید یا به کمک شکن dns خود را عوض کنید)dotnet add package Grpc.AspNetCore --version 2.44.0بعد از نصب فایل csproj شما به شکل زیر خواهد شد.2. به فایل csprojکد زیر را اضافه میکنیم:این یعنی ما سرویسی به عنوان خدمات دهنده (Server) ایجاد کرده ایم که فایل Proto آن در مسیر Protos\greet.proto است. این فایل فرمت ارتباطی را مشخص میکند. یعنی اینکه چه چیزی با چه ورودی و خروجی صدا زده خواهد شد.3.حالا این فایل proto را میسازیم. قبل از هر چیز برای اینکه ویژوال کد این فایل را بشناسد اکشتنشن زیر را نصب کنید.یک فولدر به نام Protos میسازیم و فایل زیر را درون آن ایجاد میکنیم: https://gist.github.com/2faa6269f9e29bd2324c63f564dcf198 جی آر پی سی از این فایل قراردادی برای توسعه API مرتبط استفاده میکند. این پروتکل را Protobuf می نامند(Protocol Buffer). این کدها قراردادی بین زبان های برنامه نویسی متفاوت هستند. این فایل شامل دو چیز است :تعریف سرویس gRPCپیامی که بین کلاینت ها و سرور ها ارسال میشود.در مثالی که میبینید ما سرویسی به نام Greeter تعریف کردیم که درون آن متدی به نام SayHello وجود دارد. این متد یک ریکوئست از نوع HelloRequest میفرستد که شامل یک پراپرتی از نوع استرینگ به نام name است و در پاسخ یک ریسپانس از نوع HelloReply برمیگرداند که یک پراپرتی از نوع استرینگ به نام message دارد.کافیست پروژه را با دستور dotnet build کامپایل کنید تا dll  آن ساخته شود.در مسیر obj/debug/net6.0/protos از روی فایل proto فایل های cs درست میشود که نباید در آنها تغییری ایجاد کنید چون همانطور که در تصویر زیر میبینید در بالای آن اشاره شده که این فایل هر بار در حین کامپایل به شکل خودکار ایجاد میشود و ویرایش آن بی‌فایده است.در این فولدر دو فایل وجود دارد:فایل Greet.cs که شامل همه پروتکل هاییست که برای انتشار یا سریالایز کردن ریکوئست و ریسپانس لازم استفایل GreetGrpc.cs که کلاس های کلاینت و سرور را ایجاد میکند و شامل :یک کلاس ابسترکت به نام Greeter.GreeterBase است که سرویسی که قرار است بنویسیم از این کلاس باید ارث بری کند.یک کلاس به  نام Greeter.GreeterClient که برای دسترسی نمونه های کلاس Greeter کاربرد دارد.حتما هر بار که فایل Proto را تغییر دادید و سپس پروژه را بیلد کردید و فایل جدید cs از روی Proto در      محل گفته شده ایجاد شد، یکبار Vs Code را  ببندید و باز کنید یا از Reload  Window به کمک F1 استفاده کنید. (F1 را  فشار دهید و گزینه Reload Window را  انتخاب کنید)حتا گاهی اوقات وقتی VsCode قاطی کرد از این روش برای Reload کردن صفحه استفاده کنید. یعنی دکمه F1 و سپش انتخاب Realod Windowاگر اینکار را نکنید نمیتوانید از کلاس های جنریت شده در پروژه خود استفاده کنید.4.  کلاس موجود در سرویس زیر را از روی فایل جنریت شده ارث بری کرده و متد SayHello را پیاده سازی کردیم که ریسپانس تولید کند. (اگر مرحله Reload Window را انجام ندهید کادر قرمز رنگ Greeter.GreeterBase برای Visual Code شناسایی نمیشود و نمیتوانید از آن ارث بری کنید) https://gist.github.com/266908e68d2b48e4350dde8a5836a3b2 5. این سرویس باید برای دات نت شناسانده شود پس در فایل Startup یا Program کد زیر را اضافه کنید6. چون پروژه ما api بر اساس Rest دارد باید قابلیت ارتباط با Http1و Http2 را داشته باشد. به همین منظور تنظیمات Kestrel را به appsettings.json اضافه میکنیم. https://gist.github.com/a2f3aa486486bb34e3a56c518887dc6f 7. برای تست سرویس gRPC، از یک کلاینت مرسوم استفاده کنید. مثلا BloomRpc داری GUI و بسیار ساده و کار راه انداز است.پروژه را اجرا کنید بر اساس تنظیماتی که در appsettings در شماره 6 اضافه کردیم حالت Http2 روی پورت 5003  اجرا میشود. پس آدرس را در BloomRpc روی localhost:5003 قرار میدهیم و در سمت چپ دکمه مثبت را فشرده و آدرس فایل Proto را انتخاب میکنیم و در نهایت دکمه سبز رنگ Play در وسط صفحه را میزنیم تا پاسخ را ببینیم.8. حالا که همه چیز به خوبی کار کرد سرویس اصلی برای برگرداندن قیمت کالا را مینویسیم.  7 قدم فوق را به دقت برای سرویس جدید رعایت میکنیم:ابتدا فایل price.proto را درست میکنیم. که حاوی یک سرویس  به نام Price با متد GetPrice است که ورودی آن شناسه کالا و  خروجی آن آخرین قیمت است: https://gist.github.com/9d2ffae90908cf85bdd5fd5a7af0565a این فایل را در csproj مینویسیم. https://gist.github.com/893bd0ae00fbc3188ec674bc77c49982 پروژه را بیلد کرده و Vs Code را Reload میکنیم.سرویس مرتبط با این فایل proto را میسازیم. https://gist.github.com/d3d55092e4784bbc778cac68f103d12a این سرویس را به فایل program یا startup اضافه میکنیم.تست با BloomRpcپیاده سازی سرویس کلاینت برای استفاده از gRPC Server1. پروژه  ای که قرار است از اطلاعات سرویس قیمت استفاده کند Basket.Api است. در Vs Code این پروژه را باز میکنیم. سرویسی که باید اصلاح شود UpdateBasketItems  است، یعنی اطلاعاتی از کالا در سبد ذخیره میشود که کاملا درست باشد. پس قیمت یا تخفیف از UI دریافت نمیشود. کالاها و تعدادشان از UI     دریافت شده و به کمک gRPC اطلاعات قیمتی کالاها از میکروسرویس Discount.Api گرفته میشود و در سبد ذخیره میشود (سبد در Redis نگهداری میشود. مقاله قبل را ببینید.)2. کتابخانه های زیر را به میکروسرویس Basket.Apiاضافه میکنیم.dotnet add package Grpc.Net.Clientdotnet add package Grpc.Toolsdotnet add package Google.Protobuf3. بخش زیر را به csproj اضافه میکنیم. در حقیقت به جای انتقال فایل proto به پروژه Discount به همان فایلی که ساختیم رفرنس میدهیم که اگر بعدا دستخوش تغییر شد نیازی به تغییر فایل جدید در پروژه دیگر نباشد.اینکار باعث بروز مشکلی در داکر میشود. با اجرا docker build مسیر گفته شده قابل شناسایی نخواهد بود، یعنی داکر نمیتواند خارج از کانتکستی که dockerfile در آن قرار دارد کاری کند، پس Parent Directory برای آن بی معنیست و خطای زیر را خواهیم دید.برای جلوگیری از این مشکل به ساده ترین شکل میتوانید فایل های Proto را در یک Git Repository جدا قراردهید و در دل هر ریپازیتوری که به آن نیاز بود آنها را pull کنید. با این روش هر تغییر روی Proto ها در تمامی پروژه هایی که با آن کار میکنند اعمال خواهد شد. خوشحال میشم اگر راه حل بهتری دارید برای من کامنت بذارید، من تگ هایی رو در csproj دیدم که حوزه کار docker رو تغییر میدادند ولی متاسفانه ازشون سر در نیاوردم. خلاصه اینکه مشتاقم نظرات و راه حل هاتون رو بدونم.در این مثال ما فولدر Proto را از پروژه Discount.Api به Basket.Api کپی کردیم.(گیت ریپازیتوری نساختیم و صرفا کپی کردیم) و با اینکار ساختار فایل csproj مربوط به Basket.Api به این شکل خواهد شد.4. پروژه را بیلد و Vs Code را Reload میکنیم و بررسی میکنیم که در فولدر Obj/Debug فایل price.cs و priceGrpc.cs ساخته شده باشند.5. در appsettings یک  آیتم برای ذکر آدرس gRPC Server اضافه میکنیم.6. متد آپدیت سبد به شکل زیر اصلاح میشود https://gist.github.com/d9c48acc45e1c4a354fea955dbbc6bc7 واضح است که ابتدا آدرس gRPC Server را فراخوانی کرده و یک آبجکت clientمیسازیم.سپس روی کالاهای پیمایش انجام داده و اگر قیمت برای آن از gRPC Server دریافت کردیم آن را آپدیت میکنیم.یادآوری میکنم که هندل کردن اکسپشن در یک پروژه واقعی به شکل فوق انجام نمیشود و بهتر است از یک MiddleWare برای هندل کردن تمامی اکسپشن ها استفاده کرد و یک فرمت مشخص و یکسان در خروجی داشته باشیم. همچنین صدا زدن سرویس gRPC معمولا در اکشنهای کنترلر انجام نمیشود و سرویس ها(یا هندلرها( که معمولا روال بیزنسی یک فرایند را برعهده دارند نیاز به صدا زدن gRPC Server دارند. در این مجموعه مقالات به خاطر جلوگیری از پیچیدگی از Repository و Service (handler) و ... استفاده نکردیم و تمام کارها را در کنترلر انجام دادیم.و تمام!نکاتی در مورد داکرایز کردن پروژه1. طبق معمول dockerfile و dockerignore را در مسیر اصلی پروژه میسازیم. میتوانیم از میکروسرویس Basket.Api کمک بگیریم چون آن پروژه هم دات نتی بود.دقت کنید که دو پورت باید به بیرون expose شود. 5001 و 5003 که اولی بر اساس http1و دومی بر اساس http2 کار میکند.2. دو فایل docker-compose.yml و docker-compose.override.yml را بر اساس کد زیر تغییر میدهیم. سه ایمیج جدید به این فایل ها اضافه شده، یکی مربوط به PostgreSQL و دومی هم پروژه Discount.Api     است و دیگری هم مدیریت دیتابیس Postgre به نام PgAdmin است.3. فراموش نکنید که appsetting مربوط به پروژه Basket.Api به خاطر دسترسی به gRPC Server دستکاری شد. یعنی آیتم DiscountGrpcServerUrl هم باید از localhost خارج شود و به Container مرتبط با میکروسرویس Discount.Api اشاره کند.با اینکار یک اتفاق بد رخ میدهد. وقتی پروژه Basket.Api را به منظور تست به صورت لوکال اجرا کنید و از طرفی پروژه Discount.Api هم اجرا کنید تا ارتباط gRPC را تست کنید، مجبورید این خط را تغییر دهید تا به localhost وصل شود. بهتر است. این خط را به شکل لوکالی باقی بگذارید و کانکشن استرینگ را در فایل داکر کامپوز override کنید.4. فراموش نکنید که تنظیمات appsettings را به یکی از روش های گفته شده در مقاله قبل اصلاح کنید یا به شکلی که در نکته قبلی گفته شد در فایل docker-compose محتوای appsettings را override  کنید. آدرس ها نباید مستقیم باشد و باید با صدا زدن نام Container به میکروسرویس های دیگر دسترسی داشته باشیم. مثلا در کانکشن استرینگ مرتبط با PostgreSQL در میکروسرویس Discount.Api باید به جای localhost از نام کانتینر حاوی این دیتابیس استفاده کرد. (postgresdb)	مثل نکته شماره 3 به کانکشن استرینگ داخل اپ ستینگ دست نزدیم و همان را به شکل اصلاح شده در docker-compose آوردیم. 5. برای اینکه به سوئگر در حالت داکر شده دسترسی داشته باشید تایپ داکر را در فایل داکر Development بگذارید (یا در داکر کامپوز)تعیین محیط در dockerfile که در سی شارپ میتوانید با استفاده از دستورات مرتبط با environment مقدار آن را بیرون کشیده و تصمیم درست بگیرد. در این حالت از appsettings-development استفاده میکند.تعیین محیط در docker-composeحتما نکته سوم در انتهای مقاله قبل را به دقت بخوانید. استفاده از Environment Variable ها از مطمئن ترین روش ها برای ذخیره مقادیر حساس است. دسترسی به این متغیرها در نکته سوم همان مقاله توضیح داده شده است.فایل های docker-compose در نهایت به شکل زیر خواهند شد. https://gist.github.com/729c169aa0eba85b88bdfa0d237b6580  https://gist.github.com/a0102b9a1ef005b489e892232691f71d مراحل تست ارتباط gRPC پس از اجرای docker-composeبا دستور زیر مجموعه میکروسرویس هایی که در docker-compose کانفیگ شدند را اجرا میکنیمdocker-compose -f .\docker-compose.yml -f .\docker-compose.override.yml up -d --force-recreateدر صورت نیاز با این دستور سرویس ها را متوقف میکنیمdocker-compose downدقت کنید برای تست سرویس های داکرایز شده نیاز دارید که تسلط نسبی روی ایجاد و حذف و اجرای ایمیج ها داشته باشید. همچنین باید بتوانید به کمک exec و ورود به ترمینال کانتینر دستوراتی را به منظور تست بنویسید.به محیط pgAdmin وارد شوید (از روی فایل داکر کامپوز مشخص است که به پورت 5050 مپ شده پس دستور زیر را در مرورگر مینویسیم و لاگین میکنیم)http://localhost:5050/ابتدا چک کنیم که ارتباط pgAdmin با PostgreSQL برقرار باشد.حالا دیتابیس و تیبل را میسازیم:به کمک Swagger یک رکورد به این تیبل اضافه میکنیم( کانتینر ما به سوئگر دسترسی دارد چون نوع Environment آن را Development تعیین کردیم و سوئگر در این حالت کار میکند)تا اینجای کار میکروسرویس جدید ما درست کار میکند، یعنی پورت Http1 آن که برای Rest Api است به درستی عمل کرد، همچنین ارتباط آن با دیتابیس برقرار است.با یکبار حذف کانتینر و راه اندازی مجدد docker-compose میتوانیم ببینیم که Volume آن نیز درست کار میکند. یعنی دیتابیس ساخته شده و تیبل ایجاد شده و محتوای آن حفظ میشوند چون دیتابیس را از کانتینر خارج کرده ایم.حالا به کمک پستمن متد UpdateBasketItems را تست میکنیم. (در میکروسرویس Basket.Api به سوئگر آن دسترسی نداریم چون در حالت Development داکر آن اجرا نشده است)همه چیز درست کار کرد و قیمت کالا با شناسه 1 به جای 55000  که از ورودی گرفته شده بود مقدار 10 در سبد ثبت شده است.وقتی در ارتباط gRPC به مشکل خوردیم چه کنیم؟بهتر است سوال را جامع تر مطرح کنیم. اگر در ارتباط بین دو میکروسرویس به خطا خوردیم چه کار کنیم؟ شاید ارتباط Http1 و از نوع Rest Api یا Http2 و از نوع gRPC باشد. در مقاله قبل در بخش «ساخت داکر» شماره 3 به همین مورد اشاره شد و با ورود به ترمینال Container و نصب Curl و استفاده از آن برای ارتباط برقرار کردن با دیگر میکروسرویس ها توانستیم عیب یابی کنیم.در ایجا نیز باید شبیه همان روش عمل کنید با این تفاوت که از apt-get برای نصب خبری نیست چون ابزار تست gRPC یعنی grpcurl باید مستقیم از git دانلود شود.docker exec -it basketapi /bin/bashapt-get updateapt-get install wgetwget https://github.com/fullstorydev/grpcurl/releases/download/v1.1.0/grpcurl_1.1.0_linux_x86_64.tar.gztar -xvzf grpcurl_1.1.0_linux_x86_64.tar.gzchmod +x grpcurlmv grpcurl /usr/local/bin/grpcurlgrpcurl --helpgrpcurl discountapi:5003 describeابتدا وارد ترمینال کانتینر basketapi میشویم.آپدیت apt-get را انجام میدهیم.ابزار wgetرا به منظور دریافت فایل از لینک مستقیم اینترنتی نصب میکنیم.از آدرس فایل را با wgetمیگیریم.فایل tar را باز میکنیم.دسترسی آن را به حالت اجرایی تبدیل میکنیم.فایل را به فولدر دیگری منتقل میکنیم که از همه جا در دسترس باشد.آپشن help آن را اجرا میکنیم تا از وجود و همچنین آپشن های این فایل مطمئن شویم.حالا از آپشن describe برای درک وضعیت ارتباطی میکروسرویس خود با میکروسرویس discountapi استفاده میکنیم. اگر سرویس در اینجا قطع باشد طبیعتا مشکل از کد است و بهتر است به شکل لوکالی کدهای خود را دیباگ کنید. اگر سرویس در اینجا وصل باشد به پورت های خروجی از داکر و تعریف پورت ها در appsetting میکروسرویس Discount.Api دقت کنید.کدهای مربوط به این بخش را در این ریپازیتوری از گیتهاب ببنید (کامیت پایانی تا اینجا &quot;پایان میکروسرویس قیمت و تخفیفات&quot;)</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Thu, 24 Mar 2022 20:58:37 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش میکروسرویس Microservice - پیاده سازی یک میکروسرویس برای کار با سبد خرید (بخش ششم)</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D9%85%DB%8C%DA%A9%D8%B1%D9%88%D8%B3%D8%B1%D9%88%DB%8C%D8%B3-microservice-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%DB%8C%DA%A9-%D9%85%DB%8C%DA%A9%D8%B1%D9%88%D8%B3%D8%B1%D9%88%DB%8C%D8%B3-%D8%A8%D8%B1%D8%A7%DB%8C-%DA%A9%D8%A7%D8%B1-%D8%A8%D8%A7-%D8%B3%D8%A8%D8%AF-%D8%AE%D8%B1%DB%8C%D8%AF-%D8%A8%D8%AE%D8%B4-%D8%B4%D8%B4%D9%85-n3gktwjkhqjk</link>
                <description>بخش اول : آشنایی با مفهوم میکروسرویسبخش دوم : ویژگی های اصلی یک میکروسرویسبخش سوم : تحلیل یک پروژه کوچک بر اساس میکروسرویس هابخش چهارم : شروع پیاده سازی یک پروژه فروشگاهیپیشنیاز 1 بخش پنجم : آموزش Node و Typescript برای تولید apiپیشنیاز 2 بخش پنجم : آموزش داکر و مفاهیم اولیهبخش پنجم : پیاده سازی یک میکروسرویس برای نمایش کالاها پیشنیاز 1 بخش ششم : آشنایی با Asp.net core 6 بخش ششم : پیاده سازی یک میکروسرویس برای کار با سبد خرید (همین مقاله)بخش هفتم : پیاده سازی یک میکروسرویس برای محاسبه قیمت و تخفیفاتبخش هشتم: پیاده سازی یک میکروسرویس برای ثبت سفارشبرای دیدن ویدیوهای من در مورد برنامه نویسی عضو این کانال شوید :   https://t.me/mediapub_channelدر این نوشته میخواهیم سرویسی برای ایجاد و مدیریت سبد خرید کاربر طراحی کنیم. یعنی با توجه به لیست کالاهایی که در میکروسرویس مربوط به نمایش کالا (این مقاله) طراحی کردیم، کاربر میتواند در UI کالایی را انتخاب کرده و به سبد خرید خود اضافه کند. همچنین کاربر باید بتواند لیست سبد خرید خود را در هر زمان ببینید و یا آیتمی از آن را حذف کند. پس در این میکروسرویس حداقل به سه سرویس اضافه کردن کالا به سبدخرید، حذف کالا از سبد خرید و لیست کالاهای سبد خرید نیاز داریم.همانطور که در تصویر میبینید یک فلش آبی رنگ از سمت چپ و یک فلش زرد رنگ از سمت راست کادر قرمز خارج شده اند. این دو فلش بیانگر ارتباطات داخلی بین میکروسرویس هاست که در بخش های بعدی به آن می پردازیم. همینقدر بدانید که به کمک gRPC یک ارتباط داخلی شبیه به ریکوئست/ریسپانس برقرار میکنیم. علت اینکار مشخص کردن قیمت و تخفیف کالاهای موجود در سبد است (متد Update سبد که در ادامه این متد را بدون ارتباط gRPC پیاده سازی میکنیم، فعلا از ورودی مقادیر را میگیرد) که با این روش، اطلاعات را از میکروسرویس Discount.Api دریافت میکنیم.به کمک RabbitMQ اطلاعات سبد را به سفارش تبدیل میکنیم و به تعبیری سبد را نهایی میکنیم. اینکار را اصطلاحا Checkout میگویند و وظیفه ثبت این سفارش به عهده میکروسرویس Ordering.Api است.انتظار من این است که خواننده بعد از مطالعه این نوشته بتواند موارد زیر را به راحتی تجزیه تحلیل کنید :ایجاد یک پروژه دات نتآشنایی با Redisآشنایی با اکستنشن پرکاربر MySQL در VsCodeایجاد یک CRUD ساده با دات نتکار با docker و docker-composeمهارت در استفاده از Environment Variable هااستفاده از appsettings به عنوان یک سورس بیرونی در داکرآشنایی با ترمینال لینوکس و نصب پکیجاستفاده از Curl در داخل کانتینر برای تست apiروش های پنهان کردن  مقادیر حساس در داکرآمادگی برای ارتباط این میکروسرویس با دو میکروسرویس Ordering و Discountاستفاده از Redisصرف نظر از نحوه ذخیره اطلاعات سبد، نگهداری اطلاعات سبد خرید هر کاربر و تفکیک سبدها از هم، بر اساس شناسه کاربر یا نام کاربری صورت میگیرد. ما برای ذخیره سازی از Redis استفاده خواهیم کرد. ردیس یک دیتابیس Nosql و بر اساس key/value است. یعنی میتوانیم آیتم هایی به شکل کلید/مقدار داشته باشیم. مثلا پنج کاربر زیر با سبدهایشان مشخص شده اند.ما شناسه کالا را به شکل لیست جیسونی در Value قرار داده ایم و با این اطلاعات به راحتی میتوانیم سبد خرید هر کاربر را در هر لحظه داشته باشیم. (کاربر با شناسه 1500 هیچ کالایی در سبد خود ندارد، احتمالا قبلا داشته و حذف کرده و این نتیجه یعنی ما در متد حذف، ردیف مربوط به کاربر در دیتابیس Redis را حذف نکرده ایم!)ردیس یک دیتابیس فوق العاده سریع است، چون به شکل sync کار میکند و از حافظه Ram برای ذخیره سازی استفاده میکند. ردیس دیتاتایپ های مختلفی را ساپورت میکند ومیتواند دیتا را در رم و دیسک ذخیره کند.(بر اساس آنچه در کانفیگیوریشن تعریف میکنیم) یعنی بعد از ری استارت شدن سرور هم میتوانیم دیتاها را نگه داریم و داشته باشیم. همچنین redis بسیاری از امکانات enterprise را دارد مثل sharding, Clustering, Sentinel, replicationعیوب ردیس در اشغال رم است و همچنین عدم پشتیبانی از کوئریهای پیچیده مثل حالت دیتابیس های ریلیشنال.اگر ترنزکشن شما به خطا بخورد هیچ خطای از سمت ردیس برگردانده نمیشود.برای نوشتن api ها از Asp Core 6 استفاده خواهیم کرد اما قبل از آن به کمک داکر، ایمیج Redis را به سیستممان منتقل کرده و Container آن را میسازیم تا در برنامه از آن استفاده کنیم. اگر درمورد داکر اطلاعات کمی داریم پیشنهاد میکنم این مقاله را بخوانید.Docker pull redisدقت کنید تمامی این دستورات و تنظیمات در سایت داکر نوشته شده است. کافیست docker redis را سرچ کنید.حالا با دستور زیر این ایمیج را به container تبدیل میکینیم.docker run -p 6379:6379 --name basketredis -d redisما پورت داخلی ردیس را به بیرون مپ کردیم. همچنین نام basketredis را برای این کانتینر انتخاب کردیم و از -d برای detach کردن اجرای ایمیج استفاده کردیم(در بک گراند اجرا شود و ترمینال به محض Enter زدن آزاد شود) در پایان هم نام ایمیجی که از آن قرار است کانتینر بسازیم را نوشته ایم.با اجرای دستور docker ps میبینیم که کانتینر جدیدی درست شده است. فرض کنید بقیه کانتینر ها را که در مقاله قبلی ساخته بودیم پاک کردیم.با دستور زیر از اجرای ردیس مطمئن میشویم و لاگ این کانتینر را میبینیم.میتوانیم صحت اجرای redis را با ورود به ترمنیال داخلی کانتینر بررسی کنیم.در خط اول وارد ترمینال لینوکسی کانتینر شدیم.سپس redis-cli را اجرا کردیم که محیط کنسولی برای کار با ردیس است.با دستور ping  از حاضر جوابی Redis مطمئن شدیم و به ما pong برگرداند.در نهایت با دستور set یک کلید به نام name ساختیم و به آن مقدار morteza  را نسبت دادیم. همانطور که میبینید اضافه کردن داده به سادگی با دستور set انجام میشودبه کمک get میتوانیم مقدار یک کلید را بگیریم. Get name یعنی مقداری که به کلید nameمرتبط است برگردان.با exit ِاول از redis-cli خارج شدیم و با exit ِدوم از ترمنیال لینوکسی داخلی کانتینر بیرون آمدیم.میتوانید دیتابیس خود را به کمک اکستنشنی از ویژوال استودیو که در این مقاله توضیح داده ام ببینید.بعد از فشردن دکمه کانکت در سمت چپ صفحه میتوانید دیتابیس ردیس را انتخاب کنید و کانکشن را باز کنید و دیتای داخل این دیتابیس را ببینیدتولید api برای کار با سبداگر با asp.core آشنایی ندارید پیشنهاد میکنم این ویدیوی آموزشی را ببینید.(در یوتیوب به زبان فارسی)ابتدا هدف ما این است که سرویسی بنویسیم که شناسه کاربر را بدهیم و اطلاعات سبد کاربر را دریافت کنیم. برای کار با دات نت کور بهترین IDE برنامه‌ی Visual Studio است. اما چون ممکن است مشتاق نصب آن به لحاظ فضای دیسک و حافظه نباشید از همان Visual Code استفاده خواهیم کرد.برای کار با دات نت لازم است SDK آن را نصب کنید. (برای کار با javascript به عنوان یک زبان برنامه نویسی Back-end نیز لازم بود Node js را نصب کنید) لینک دانلود دات نت 6بعد از نصب به کمک دستور زیر از نسخه نصب شده مطمئن شوید.dotnet --version3. با  دستور زیر یک پروژه webApi به نام Basket.Api بسازیدو بطور خودکار فولدر آن نیز ساخته میشود. دقت کنید دستور را جایی  بنویسید که فولدر Basket.Api در  کنار Catalog.Api  ساخته شود.آدرسی که در آن پروژه را میسازید را درست و در محل مناسب و در کنار بقیه میکروسرویس ها انتخاب کنید.4. پروژه را با دستور dotnet run  اجرا میکنیم.روت(آدرس) weatherforecast یک api برای مثال است که در تمپلیت پیش فرض دات نت وجود دارد و خروجی زیر را برمیگرداند:در تصویر زیر کد مربوط به این api  را میبینید.متدهای پیش فرض تمپلیت دات نتتوضیح مختصر اینکه در فولدر کنترلر فایل هایی با قاعده نامگذاری مثل آنچه میبینید درست میکنیم. مثلا چون در این میکروسرویس قرار است با Basket کار کنیم فایل BasketController  را میسازیم. در این فایل که کنترلر نام دارد متدهایی میسازیم که وظایف مختلف دارند. هر کدام از این متدها به یک روت (Route) مپ میشوند که بصورت پیش فرض هم نام متد هستند.5. میخواهیم متدهایی با وظایف زیر بنویسیممتدی برای دریافت محتویات سبدمتدی برای اضافه کردن یا آپدیت کردن سبدمتدی برای حذف یک آیتم از سبدمتد برای checkout کردن سبداگر قرار بود تمامی قواعد Clean Architecture را رعایت کنیم و Repository Pattern  در پروژه مان پیاده سازی شود نیاز به مقالات بیشتر و توضیحات کامل تر بود اما در این نوشته از این جزئیات صرف نظر میکنیم و بدون رعایت لایه بندی، در کنترلر متدها را پیاده سازی میکنیم. (برای مطالعه اطلاعات بیشتر این مقاله را ببینید).این نکته را هم در نظر بگیرید که هدف از معماری ها و پترن ها چیست؟ آیا در یک میکروسرویس ساده که وظیفه محدودی برای آن تعریف شده و قرار است تا ابد همین وظیفه محدود را به عهده داشته باشد و شاید تعداد خطوط کد آن از 100 فراتر نرود، نیاز به رعایت قواعد و قوانین دست و پا گیر است؟ 6. یک مدل برای سبد تعریف میکنیم و در فولدر Models قرار میدهیم. https://gist.github.com/ed7cbe0952aff45fba26711afa2d75f8 7. کتابخانه دسترسی به Redis را در دات نت نصب میکنیم.dotnet add package Microsoft.Extensions.Caching.StackExchangeRedisکتابخانه مربوط به ارتباط Redisهمانطور که میبینید بعد از اجرای دستور فوق، خط مشخص شده به فایل csproj اضافه میشود.تنظیمات استفاده از ردیس به شکل زیر است. در فایل program.cs  (برای نسخه 6 دات نت) و یا Startup.cs (برای نسخه های قدیمی تر)  به شکل زیر نحوه اتصال به ردیس را کانفیگ میکنیم.هر دیتابیسی نیاز به معرفی در ابتدای اجرای برنامه دارداین بخش را به خاطر بسپرید. اگر مقاله قبل را مطالعه کرده باشید میدانید که نباید به شکل صریح به localhost در تنظیمات اشاره کرد. اینکار داکر را به دردسر می اندازد. در همین مقاله این مشکل را حل خواهیم کرد.8. با ملاحظاتی که در ادامه به آن اشاره خواهم کرد، کنترلر مربوط به وظایف Basket را به شکل زیر ایجاد      میکنیم. https://gist.github.com/bc986ed37b18ba1dc45b8f6a496067d8 دقت کنید ما اطلاعات کالا را برای ثبت در سبد از بیرون به عنوان ورودی میگیریم. یعنی مثلا کاربری با یوزرنیم mortezadalil سبدی با 5 کالا دارد که شناسه های کالا به شکل 110و112و122و134و115 است. در مدل BasketItem ما پراپرتی قیمت و تعداد داریم. پس این دو مقدار هم به ازای هر کالا از ورودی میگیریم اما طبیعتا مقدار قیمت برای ما سندیت ندارد. قیمت بر اساس آنچه در رابط کاربری به کاربر نمایش داده میشود برای ما ارسال میشود و چون این مقدار توسط کاربر قابل دستکاری است (از طریق Postman یا حتا تغییر Dom در رابط کاربری) پس تصمیم درست این است که این مقدار را بی اهمیت تلقی کنیم و بر اساس شناسه کالا و تعداد آن که از UI  به عنوان ورودی دریافت میکنیم یک درخواست جدید بسازیم و به میکروسرویس مرتبط با قیمت و تخفیف یعنی Discount.Api  ارسال کنیم و به تعبیری قیمت واقعی محصول را از سرور استعلام کنیم و بر اساس قیمت درست و تخفیف احتمالی سبد را در Redis به روز رسانی کنیم. اینکار را در آینده به کمک  gRPC و ارتباط برقرارکردن با میکروسرویس مختص اینکار، انجام خواهیم داد. (این مقاله)همچنین ما متد پیاده سازی نشده ای به نام Checkout داریم که برای قطعی کردن سبد به منظور خرید استفاده میشود. این متد با میکروسرویس Order.Api  ارتباط برقرار میکند و سفارش را ثبت میکند. پیاده سازی این بخش را در آینده انجام خواهیم داد و از RabbitMQ  استفاده خواهیم کرد. (این مقاله)9. با دستور dotnet run پروژه را اجرا میکنیم یا میتوانیم F5 را  در Visual Code  فشار دهیم. دقت کنید برای اجرا به کمک F5 بهتر است که ویژوال کد داخل پروژه دات نت اجرا شود. یعنی ما در پنجره explorer مربوط به visual code محتویات فولدر Basket.Api را  ببینیم و نه همه ی میکروسرویس ها.بهتر است هر میکروسرویس فولدر vscode. مربوط به خودش را داشته باشد. ( با فشردن F5 از شما میپرسد که اجرای پروژه به چه شکل باشد. روشی که انتخاب میکنید باعث ایجاد فولدر .vscode میشود.)طوری ویژوال کد را اجرا کنید که فقط پروژه ای را ببینید که روی آن کار میکنید و نه همه پروژه ها10. حالا با پستمن متدهایی که نوشتیم را تست میکنیمهنوز سبدی برای کاربر mortezadalil ساخته نشده است. پس خالی یا null  برگردانده میشود.حالا سبد این کاربر را با آنچه در پستمن تعریف کرده ایم پر میکنیم.به کمک برک پوینت میتوانیم مقادیر را ببینیم و رصد کنیممشاهده مقادیر به کمک برک پوینتتست آپدیت یک سبد خریدهمانطور که قبلا گفته شد برای متد آپدیت شناسه کالا(یا کالاها) ،تعدادشان و نام کاربر اهمیت دارد. قیمت و نام کالا هیچ استفاده ای نمیشود. درست است که فعلا همین مقادیر ورودی را در Redis  به نام این کاربر ذخیره میکنیم اما در مقالات بعدی قرار است قیمت به کمک میکروسرویس Discount.Api  به شکل مطمئن دریافت و در ردیس ذخیره شود. همچنین نام کالا هم به همین شکل قابل بررسی و به روزرسانی است. دلیل ذخیره سازی قیمت و نام کالا در سبد به این منظور است که برای هر فرد زمانی که سبد وی فراخوانی میشود هر بار نیاز به استعلام نام و قیمت از Discount.Api نباشد. با اینکار ما در کوئری گرفتن از دیتابیس صرفه جویی میکنیم و از طرفی آنچه در سبد داریم نیز معتبر است.ساخت فایل داکر1. قبل از هر چیز یک فایل به نام .dockerignore درست میکنیم و فولدر bin و obj را در آن به شکل زیر تعریف میکنیم.**/bin/
**/obj/اگر اینکار را نکنیم بعد از ایجاد dockerfile و هنگام اجرای دستور docker build ایمیج داکر ایجاد نمیشود و به خطا برخورد خواهیم کرد.2. حالا فایل داکر را به شکل زیر درست میکنیم.(این داکر فایل یک مشکل دارد که در ادامه آن را تصحیح میکنیم) https://gist.github.com/819d36cdc5f6f4ebbf587e08888bde58 3. دستور زیر را برای  ایجاد ایمیج از روی فایل داکر فوق مینویسیمdocker build . -t basketapi:0.0.1با دستور زیر ایمیج داکر را اجرا میکنیم.docker run -d -p 4600:80 --name basketapi  basketapi:0.0.1فرض ما این است که دستور dotnet basket.api.dll به شکل production و روی پورت 80 اجرا میشود و ما 80 داخلی را به 4600 بیرونی مپ میکنیم.اما پس از اجرا میبینیم که روی localhost:4600 ریسپانس نمیگیریم.هیچ پاسخی دریافت نکردیم. اصلا ریکوئست به جایی نرسیده که پیغام درستی ببینیم!برای پیدا کردن مشکل ابتدا لاگ را بررسی میکنیمdocker logs basketapiاگر مشکلی وجود نداشت و روی پورت 80 اجرا شده بود حالا بررسی میکنیم آیا میکروسرویس درون خود container به درستی اجرا شده و میتوان api ها را صدا کرد.با دستور ls بررسی میکنیم که فایل ها درست در این داکر وجود داشته باشند.ظاهر تا اینجا مشکلی نیست. برای تست api باید برنامه curl را در محیط لینوکسی کانتینر داشته باشیم که نداریم! میخواهیم در ترمینال کانتینر از curl استفاده کنیم.پس به کمک دستورات apt-get update ابتدا پکیج ها را آبدیت و سپس به کمک ap-get install curlپکیج مورد نظر یعنی curlرا از اینترنت میگیریمحالا از پستمن curl ریکوئست را میگیریم.کرل به شکل زیر است:curl --location --request GET &#039;http://localhost:4600/basket/GetBasketItems/mortezadalil&#039;طبیعتا جواب نمیگیریم چون در داخل لینوکس با پورت خارجی کرل را صدا زدیم.با پورت 80 اینکار را میکنیم:curl --location --request GET &#039;http://localhost:80/basket/GetBasketItems/mortezadalil&#039;اینبار خطا گرفتیم یعنی آدرس و پورت درست است و به هر دلیلی Internal Server Error گرفتیم. میخواهید دلیلش را بدانید؟ باید از لاگ دات نت استفاده کنیم. یک تب جدید ترمینال درست میکنیم و لاگ این کانتینر را رصد میکنیم.docker logs --tail 1 basketapiیعنی یک خطای آخر یا یک خط آخر را برگردان.در متن خطا مشخص است که ارتباط برقرار شده ولی چون نمیتواند با Redis کانکشن برقرار کند خطا میدهد و خطای 500 به این دلیل بود.برگردیم به مشکل اصلی ، ما داخل کانتینر، پروژه را به شکل اجرا شده داریم و پورت 80 داخلی درست کار میکند. اما به پورت بیرونی 4600 متصل نشده است که از بیرون به آن دسترسی داشته باشیم.پورت اجرایی داخلی را با اضافه کردن خط زیر در داکر فایل میتوان تغییر داد.ENV ASPNETCORE_URLS=http://+:5000داکر فایل به شکل زیر خواهد شد: https://gist.github.com/daeaacb1088e00577f1681718f778528 دقت کنید این عمل با روش های دیگر مثل تغییر در program.cs و appsettings.json نیز ممکن است ولی چه بهتر که برنامه مان را درگیر این کارها نکنیم!کانتینر و ایمیج خود را پاک میکنیم. (به کمک دستور یا docker desktop قابل انجام است در مورد دستورات حذف ایمیج یا کانتینر در این مقاله صحبت کردیم)انیمیشن زیر حذف با docker desktop را نشان میدهد.توجه داشته باشید که داکر یک ابزار است که توسط ترمینال همه جور آپشنی را در اختیار شما قرار میدهد. داکر دسکتاپ یک رابط کاربری نسبتا خوب برای داکر است که شما را از درگیری با دستورات ترمینال تا اندازه ای دوره میکند. درست است که سفارش شده از دستورات خود داکر استفاده کنید تا دستورات را فراموش نکنید اما مطلع باشید که تسلط خداگونه به داکر و کوبرنتیز و فهمیدن جزئیات آنها هیچ ارتباطی به مهارت پیاده سازی بیزنس های پیچیده برنامه نویسی ندارد. به همین دلیل خیلی وقتتان را با خواندن مطالبی در مورد ابزارها (مثل Git و Docker و ... ) تلف نکنید دستورات اینها در حد Regex بیخود و فراموش شدنی است. قابلیت ها را پیدا کنید ولی حفظ نکنید. حین انجام پروژه با این ابزار کار کنید، سرچ کنید و مشکلتان را حل کنید. همینقدر کافیست. از داکر دسکتاپ هم به اندازه استفاده کنید :)حالا پروژه را دوباره بیلد و اجرا میکنیم.مشکل بر طرف خواهد شد و در پستمن خطای 500 مربوط به عدم برقراری ارتباط با redisرا میبینیم که از طریق docker logs جزئیات این خطا قابل دسترسی است.اما چطور این خطای کانکشن را برطرف کنیم؟ مقاله قبل را خوانده اید؟ دو نکته در آن مقاله در همین زمینه گفته شد.ابتدا مطمئن  شوید که در یک network کانتینرها اجرا شده اند.صدا زدن یک کانتینر دیگر باید با اسم آن کانتینر انجام شود.(یعنی appsettings را اصلاح میکنیم)این دو مورد را در مقاله قبل تست کردیم و شما هم میتوانید دستورات ایجاد کانتینر را به همراه نام network برای Redis و basketApi باز نویسی کنید. (برای اینکار هر دو کانتینر را پاک کنید).راه حل بهتر برای این مشکلات همانطور که در مقاله قبل به آن اشاره شد استفاده از docker-compose است. بدون هیچ توضیح اضافی داکر کامپوزی که در مقاله قبلی تهیه کردیم را کامل میکنیم. دقت میکنیم که appsettings را به شکل یک فایل خارجی به کمک volume تعریف میکنیم که تغییر و ری استارت کانتینر باعث اعمال تغییرات appsettings در پروژه شود. https://gist.github.com/27b24fda98a40bf5540027bbcf14b551  https://gist.github.com/ff5686ffa029babb2fcac2121e398253 همه چیز به خوبی و خوشی اجرا میشود.نکته اول: در کانفیگ داکر کامپوز سعی کنید آدرس ها را نسبی ثبت کنید. مثلا خط زیر برای مشخص کردن مسیر volume باید اصلاح شود:./ecommerceMicroservice/Basket.Api/appsettings.json:/app/appsettings.jsonبه شکل زیر باید تبیدل شود:./Basket.Api/appsettings.json:/app/appsettings.jsonچون این ایمیج ها برای هر سیستمی باید قابلیت بیلد شدن داشته باشند. وقتی مسیر روی یک سیستم وجود نداشته باشد بیلد شدن با مشکل روبرو خواهد شد. فایل docker-compose محل اصلی برای اجرای دستورات داکر است و ریشه محسوب میشود و مسیر ها باید به نسبت این فایل تعیین شود.نکته دوم : دقت کنید که هر بار appsettings را در سیستم خودتان در محلی که آدرس آن را به docker-compose داده اید تغییر دهید باید داکرکامپوز را یکبار ری استارت کنید و بهتر است از دستور زیر برای اینکار استفاده کنیدdocker-compose -f .\docker-compose.yml -f .\docker-compose.override.yml up -d --force-recreateنکته سوم:اگر اطلاعات مهمی مثل پسورد یا private key در appsettings دارید بهتر است در همان حالت لوکال از آن استفاده کنید.برای حالت پروداکشن به یکی از روش های زیر عمل کنید.روش 1: برای پروداکشن از appsettings مربوط به پروداکشن استفاده کنید که تنظیمات آن در دات نت وجود دارد.(این مقاله را ببینید)در مقاله فوق توضیح داده شده که چطور چندین پروفایل در دات نت داشته باشیم (مثلا برای development, production, staging) . طبیعتا اگر چند پروفایل دارید حتما در dockerfile باید ASPNETCORE_ENVIRONMENT را مشخص کنید.روش 2 : اگر حال استفاده از دو apsettings ندارید میتوانید در کد به کمک دستورات مرتبط با Environment ابتدا مطلع شوید کجا هستید و سپس تصمیم بگیرید از appsetting استفاده کنید یا از variable ها. مثلا در program.cs به شکل زیر میتوان وضعیت جاری پروژه را فهمید و تصمیم گرفت.در محل های دیگر نیز میتوان از دستور زیر در سی شارپ استفاده کردEnvironment.GetEnvironmentVariable(&amp;quotASPNETCORE_ENVIRONMENT&amp;quot)روش 3: این روش پیشنهادی و بهینه بدون نیاز به چند appsettings است. در حالت لوکال env ها را به روش زیر تعریف میکنیم. مثلا برای کانکشن ردیس به شکل زیر تعریف میکنیم و در powershell به همین شکل مینویسیم. این دستور در لینوکس متفاوت است. (بعدا این متغیرها را در فایل داکر یا داکر کامپوز به طریقی که در روش 1 گفته شد باید تعریف کنید و سپس ایمیج بسازید)$env:REDIS_CONNECTION = &#039;redisdb:6379&#039;در appsetting کلیدهای مربوط به environment variable ها را(که قبلا در سیستم تعریف کرده اید) به عنوان value قراردهیددر کد ابتدا با متد GetValue از کلاس Configuration این Valueها را بیرون بکشید. حالا این Value برای Environment Variable کلید محسوب میشوند. با دستور GetEvironmentVariable که در روش 2 گفتیم مقدار آن را بیرون بکشید. در تصویر زیر استفاده از این روش را در program.cs برای معرفی آدرس کانکشن ردیس میبینیم.امیدوارم این مقاله گره ای از مشکلات شما را باز کرده باشد. سعی کردم تمامی مشکلاتی که حین تولید میکروسرویس با دات نت به آن برخورد میکنید ضمن شرح مشکل، پاسخ دهم. در مقالات بعدی دو میکروسرویس باقیمانده را طراحی و پیاده سازی خواهیم کرد. همچنین به نحوه ارتباط بین میکروسرویس ها از طریق gRPC و RabbitMQ نیز خواهیم پرداخت.کدهای مربوط به این بخش را در این ریپازیتوری از گیتهاب ببنید (کامیت پایانی تا اینجا &quot;پایان میکروسرویس سبد خرید&quot;)</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Wed, 02 Feb 2022 19:23:09 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش میکروسرویس Microservice - پیاده سازی یک میکروسرویس برای نمایش کالاها (بخش پنجم)</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D9%85%DB%8C%DA%A9%D8%B1%D9%88%D8%B3%D8%B1%D9%88%DB%8C%D8%B3-microservice-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%DB%8C%DA%A9-%D9%85%DB%8C%DA%A9%D8%B1%D9%88%D8%B3%D8%B1%D9%88%DB%8C%D8%B3-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%86%D9%85%D8%A7%DB%8C%D8%B4-%DA%A9%D8%A7%D9%84%D8%A7%D9%87%D8%A7-%D8%A8%D8%AE%D8%B4-%D9%BE%D9%86%D8%AC%D9%85-jusmvtr5taz9</link>
                <description>بخش اول : آشنایی با مفهوم میکروسرویسبخش دوم : ویژگی های اصلی یک میکروسرویسبخش سوم : تحلیل یک پروژه کوچک بر اساس میکروسرویس هابخش چهارم : شروع پیاده سازی یک پروژه فروشگاهی پیشنیاز 1 بخش پنجم : آموزش Node و Typescript برای تولید apiپیشنیاز 2 بخش پنجم : آموزش داکر و مفاهیم اولیهبخش پنجم : پیاده سازی یک میکروسرویس برای نمایش کالاها (همین مقاله)پیشنیاز 1 بخش ششم : آشنایی با Asp.net core 6 بخش ششم : پیاده سازی یک میکروسرویس برای کار با سبد خریدبخش هفتم : پیاده سازی یک میکروسرویس برای محاسبه قیمت و تخفیفاتبخش هشتم: پیاده سازی یک میکروسرویس برای ثبت سفارشبرای دیدن ویدیوهای من در مورد برنامه نویسی عضو این کانال شوید :   https://t.me/mediapub_channelبخش چهارم را به عنوان مقدمه برای این بخش بخوانید.میخواهیم به کمک یک پروژه Node و با استفاده از ابزار Typescript و دیتابیس Mongo یک پروژه ساده برای دسترسی و تغییر کالاها ایجاد کنیم و در نهایت به کمک داکر و docker-compose نحوه داکرایز کردن این مجموعه را بررسی کنیم. اگر برنامه نویس جاوااسکریپت نیستید اصلا نگران نباشید، ذهن خودتونو آزاد کنید و با من همراه باشید تا به کمک Node (ابزار ران تایم جاوااسکریپت) یک پروژه کوچک طراحی کنیم.دقت کنید که تخصص در زبان برنامه نویسی جاوااسکریپت برای خواندن این مقاله لازم نیست.با مطالعه این مقاله، روش داکرایز کردن را برای پایتون و دات نت و php و ... هم یاد میگیرید ثانیا در مقالات بعدی با فریم ورک ها و زبان های برنامه نویسیِ مختلف میکروسرویس مینویسیم و داکرایز کردن آن ها را نیز بررسی میکنیم.انتظار من این است که خواننده بعد از مطالعه این نوشته بتواند موارد زیر را به راحتی تجزیه و تحلیل کنید :ایجاد یک پروژه Node js به کمک express و typescript به منظور تولید apiایجاد یک CRUD ساده به کمک node/express/typescript/mongooseساخت فایل dockerدرک و ایجاد docker imageدرک و ایجاد docker containerدرک و ایجاد docker-composeآشنایی با مفهوم volume و  Bind mount در داکرآشنایی با مفهوم شبکه در داکرآشنایی با دستورات ایجاد و حذف image و ایجاد و حذف containerآشنایی با سکشن های docker-composeاگر با Node js آشنایی ندارید و نمیدانید چطور یک پروژه Node/Typescript بسازید این مقاله را بخوانید و سپس ادامه نوشته را پیگیری کنید. من این پروژه را به شکل آماده در Github قرار داده ایم (این لینک). برای دسترسی به تمپلیت اولیه که در مقاله فوق آن را پیاده کرده ایم کامیت اول ریپازیتوری موجود در github را ببینید.برای دسترسی به دیتابیس در پروژه Node از Mongoos استفاده میکنیم که بهتر است در محل کار آن را مانگوووس (با تکید روی س) صدا کنید. (برای خودتون میگم).بدون فوت وقت به ایجاد یک CRUD ساده برای کار با کالا می پردازیم.(Create/Read/Update/Delete) قبل از هر چیز روش ارتباط با Mongo را در یک پروژه Node/Typescript/Express بررسی میکنیم.1. مطمئن شوید که mongo را نصب کرده اید. (برای انجام جزئیات این مرحله، این مقاله را بخوانید، در انتهای مقاله به روش نصب mongo از طریق داکر اشاره شده است)2. روی پروژه ای که به شکل تمپلیت از Node/Typescript/Express تهیه کرده ایم کتابخانه Mongoos را نصب کنید.npm i mongooseعبارت فوق را در ترمینال Visual code مینویسیم (فرض من این است که شما مقاله مربوط به ساخت تمپلیت Node با Typescript را مطالعه کرده اید. در حقیقت ما در حال تکمیل آن پروژه هستیم)نصب mongoose3. برای اینکه تایپ اسکریپت یک دسترسی خوش ساخت به mongoos داشته باشد و بتواند با interface ها به ما کمک کند (متدهای دسترسی را با hint و intellisense ببینیم)، defenition type مربوط به mongoos را به کمک دستور زیر نصب میکنیم.npm i --save-dev @types/mongooseاز save-dev برای این استفاده میکنیم که ابزار توسعه را از کتابخانه های کاربردی جدا کنیم. این اتفاق در package.json می افتد. دو بخش جدا برای dependencies و devdependencies درست میشود. ابزار توسعه نیازی به انتقال حین build شدن ندارند. یعنی بعد از build، وجود این type ها ارزشی ندارد.4.کنترلر و روت جدیدی در پروژه تمپلیت میسازیم تا لیست کالا ها را دریافت کنیم. https://gist.github.com/3126b1d8c3f15dbb343f3daeaa18ee48 در فایل product.ts موجود در فولدر controllers متدی که لیست کالاها را برگرداند میسازیم. فعلا آن را پیاده سازی نمیکنیم. https://gist.github.com/2c4bc57e3eb754c54aaaf3c598e91ea6 روت getAllProducts را در فایل product.ts موجود در فولدر routes تعریف میکنیم که به controller.getAllProducts متصل است.در فایل server.ts هم یک روت جدید برای ارتباط با فایل route بالا تعریف میکنیم. روت های جدید را فقط در فایل routes/product.ts اضافه میکنیم و همه به نوعی Sub-Route آنچیزی هستند که در server.ts تعریف کرده ایم.5. به کانفیگ پروژه اطلاعات اتصال به مانگو را اضافه میکنیم. MONGO_OPTIONS و MONGO_HOST و MONGO ثوابتی هستند که ایجاد کردیم و در نهایت ثابت MONGO را به ثابت CONFIG در پایین فایل config.ts اضافه میکنیم. https://gist.github.com/bd85a29f94291e53d2bc184e77d5961e 6. در ادامه میخواهیم در فایل Server.ts به Mongo بوسیله Mongoose متصل شویم و کانکشن برقرار کنیم. https://gist.github.com/058a0004bb25a0a2dd12f84c6d57439b 7. فولدری به نام interfaces میسازیم. میخواهیم مدلی را بسازیم که به کمک آن مانگوس از ساختار product مطلع شود. https://gist.github.com/fabfdf61540d45a1f08f2cb334efc1ee 8. حالا اسکیمای product را در فولدر models میسازیم. https://gist.github.com/4feaa83d6472b1aa3e51a9b302998d89 timestamps : به طور خودکار تاریخ را در داکیومنت مونگو ثبت میکند9. حالا کنترلر product را تکمیل میکنیم. https://gist.github.com/6b2fa04d21ba91349a4bce7fbd315cb9 10. متد list را تست میکنیم. قبل از تست لازم است پروژه را با دستور زیر اجرا کنیم :nodemon source/server.tsهنوز دیتایی نداریم.11. با فشردن ctrl+c در ترمینال visual code میتوانیم اجرا را متوقف کنیم. متد createPost را برای ایجاد آیتم در دیتابیس درست کرده ایم. همچنین متدهای لازم برای یک crud را مینویسیم. برای این متدها روت های مناسب را در فایل routes/product.ts تعیین میکنیم. https://gist.github.com/e846a29f97ff5414fb9d80304f8e4ab9 12. پروژه را با دستور شماره 10 دوباره اجرا کنیم.13.برای تست متد add چون POST است از ابزاری مثل Postman استفاده میکنیم.بخش هایی که علامت گذاشتیم را به همین شکل در پستمن تکمیل میکنیم و ورودی را به شکل فوق مینویسیم و خروجی نشان میدهد که در دیتابیس یک رکورد (داکیومنت) ثبت شده است.میتوانید به کمک ابزار دیگری مثل compass یا Studio 3T محتوای دیتابیس را چک کنید14. با تست put خواهیم دید.متد ویرایشمتد حذفبه اهدافی که تعیین کرده بودیم در پروژه رسیدیم. CRUD با موفقیت اجرا میشود.ساخت فایل داکرقبل از هرکاری مطمئن باشید vpn شما متصل است. داکر برای گرفتن کتابخانه های مورد نیاز گاهی بدون وی پی ان به مشکل میخورد.ابتدا فایل داکر را میسازیم. https://gist.github.com/6c8b984d2e9bd44df2468dbf53593016 به محل قرار گیری dockerfile دقت کنید.برای ساخت فایل ایمیج دستور زیر را در محلی که فایل package.json قرار دارد مینویسیم:docker build . -t catalogapiو برای ساختن کانتینر از این ایمیج از دستور زیر استفاده میکنیم و پورت 4300 داخلی را به 4500 خارجی مپ میکنیم (catalogapi اولی اسم کانتینر و دومی اسم imageی است که کانتینر از روی آن ساخته میشود):docker run -d -p 4500:4300 --name catalogapi catalogapiو میبینیم که پروژه اجرا میشود.برای دیدن لاگ ِ کانتینر دستور زیر را مینویسیم:docker logs -f a18b3a98e3faمقدار a18b3a98e3fan شناسه کانتینر ماست که به کمک دستور docker ps قابل دستیابی است.docker psخروجی لاگ یک خطا نشان میدهد.خطایی که به دلیل عدم دسترسی به دیتابیس میبینیمخطای فوق یعنی سرویس ما نمیتواند به monogo متصل شود و طبیعیست. چون ما پروژه را داکرایز کردیم و در کانتینر ما، فولدر build داخل یک سیستم لینوکسی قرار دارد و در داخل یک سیستم لینوکسی پروژه اجرا شده است. پورت 4300 که پورت اجرا شده داخلی است را به 4500 خارجی مپ کردیم که از بیرون به api ها دسترسی داشته باشیم. با این اوصاف واضح است این کانیتنر (یعنی catalog) به mongo که خودش یک کانتینر دیگر است دسترسی ندارد و همدیگر را نمیبینند. ( در مرحله 1 در این مقاله ما mongo را داکرایز کردیم).یک راه حل برای این مشکل مشخص کردن نام یک container برای container دیگر است. در تصویر فوق صراحتا نوشته شده که ارتباط با 127.0.0.1:27017 ممکن نیست  ولی حتما باید ارتباط با mongo:27017 ممکن باشد (فرض کنید mongo اسم کانتینر مربوط به مانگو است) پس در کد به جای 127.0.0.1 اسم کانتینر را مینویسیم. اما بهتر است اینکار را با تعریف environment انجام دهیم. در کد نیز این قابلیت (استفاده از environment ها ) را به نام MONGO_URL لحاظ کرده ایم. پس کافیست موقع اجرای ایمیج مربوط به catalog.api این کلید از environment را تعریف میکنیم.docker run -d --network node-webapp-network --name mongodb mongo
docker run -d -p 4500:4300 --env &#039;MONGO_URL=mongodb:27017&#039; --network node-webapp-network01 --name catalogapi catalogapiدستور اول، داکرِ مربوط به ایمیجِ mongo را به نام mongodb اجرا میکند.دستور دوم داکر مربوط به ایمیج ِcatalogapi را به نام catalogapi اجرا میکند و یک آیتم environment vatraible هم به نام MONGO_URL تعریف میکند و مقدار میدهد.نکته مبهم ولی ساده در این دو دستور آپشن network است که باید در هر دو یکسان باشد، یعنی هر دو در یک شبکه باشند که بتوانند همدیگر را ببینند. این شبکه را باید قبل از اجرای دو دستور فوق با دستور زیر بسازیم.docker network create node-webapp-networkدر تصویر اول دو کانتینر در دو نتورک جدا هستند و ارتباط ممکن نیست. در صویر دوم یک شبکه برای هر دو کانتینر تعریف کرده ایم و امکان برقراری ارتباط بین دو کانتینر توسط نام کانتینرها برقرار است.اما راه حل ساده تر برای یکسان سازی شبکه و همینطور مدیریت ارتباط بین میکروسرویس ها استفاده از docker-compose  است.ساخت فایل docker-composeداکر کامپوز مسئول ایجاد، اجرا و مدیریت ارتباط بین میکروسرویس هاست. چون برای همه است، پس محل قرار گیری آن نباید داخل فولدر این میکروسرویس باشد بلکه یک سطح عقب تر باید قرار داشته باشد.(که برای دسترسی به میکروسرویس های دیگر به زحمت نیفتیم و به میکروسرویس های دیگر هم دسترسی داشته باشد)محل درست قرارگیری فایل docker-composeیک فایل docker-compose.yaml با بخش های اصلی میسازیم و برای محیط های مختلف مثل development یا production این فایل را override میکنیم. چون فعلا هدف ما development است یک فایل docker-compose.override.yaml هم میسازیم.محتوای یک فایل کامپوز به شکل زیر است. https://gist.github.com/b0f2bc71b8e4ed999627643959bf6b37  https://gist.github.com/31088ec45ff14cee3cb3db896c2f8c7a همانطور که میبینید ما به ازای هر سرویس یک سکشن داریم. فعلا دو سرویس یکی مخصوص دیتابیس مونگو و دیگری مخصوص به catalog داریم که نامگذاری دلخواه میکنیم و در docker-compose و docker-compose.override آنها را به شکل فوق تکمیل میکنیم.نکات :ورژن داکر کامپوزی که از آن استفاده میکنیم در بالای فایل نوشته شده است و 3 است.اجرای داکر کامپوز در محلی که  فایل کامپوز قرار دارد انجام میشود، اگر ایمیج ها وجود نداشته باشند ابتدا pull میشوند(مثل mongo) و چنانچه برای آنها محلی به عنوان context (بخش context را در فایل فوق ببینید) معرفی شده باشد با پیدا کردن dockerfile ، ایمیج آنها ساخته میشود. اگرایمیج آنها قبلا ساخته شده باشد دوباره ساخته نخواهند شد. این اطلاعات به همراه نام image در فایل docker-compose.yml قرار دارد چنانچه این اطلاعات در development شما با production شما متفاوت است نباید آنها را در  فایل docker-compose.yml بنویسید و  باید آن را در override ها ذکر کنید.در فایل docker-compose-orverride.yaml همان سکشن های  مربوط به سرویس ها تکرار شده اند اما محتوای آنها متفاوت است و نیاز به تکرار  آنچه در docker-compose اصلی نوشته ایم نیست.در داکر کامپوز باید مشخص شود چه پورت داخلی از کانتینر به بیرون مپ میشود. مثلا چون پورت      دیفالت داخلی مانگو 27017 است آن را به یک پورت بیرونی مپ کرده ایم (مثلا در اینجا همان شماره را برای پورت بیرونی درنظر گرفته ایم) یا در سرویس catalog.api چون node/express ما روی پورت 4300  اجرا میشود آن را به یک پورت در خارج مپ کرده ایم. 4000 پورت انتخابی ماست و بوسیله پستمن بعد از ایجاد کانتینر به این پورت دسترسی داریم.سکشن مهم depends_on مشخص میکند که اجرای این سرویس به کدام  سرویس وابستگی دارد. یک نوع dependency  order را حین اجرای ایمیج ها رعایت میکند. در اینجا یعنی catalog.api بعد از اجرای catalog.db اجرا میشوند.سکشن environment برای سرویس هایی که از environment چیزی میخوانند لازم است. مثلا ما در سرویس catalog.api بخشی از اطلاعات اتصال دیتابیس را از environment میخوانیم.بخش volumes برای سکشن هایی که نیاز به ذخیره اطلاعات روی دیسک دارند لازم است. درادامه این بخش را توضیح خواهم داد.انواع روش های ذخیره سازی در داکردر مورد volume در این مقاله به اختصار صحبت کردیم. گفتیم چون کانتینری که از ایمیج ساخته میشود به لحاظ پایداری مطمئن نیست و ممکن است Stop یا Remove شود (توسط ما یا به دلیل مشکلات نرم افزاری) بهتر است دیتاها را در جای دیگری نگه داریم. مثلا اگر سرویس آپلود عکس داریم دلیلی ندارد در خود کانتینر عکس ها را نگه داریم، چون با حذف کانتینر، تصاویر نیز از بین میروند. مثلا فرض کنید داخل کانتینر یک فولدر images داریم که عکس ها در آن قرار میگیرند. کار درست این است که این فولدر را به یک فولدر خارجی یا یک docker volume خارجی مپ کنیم که هر اتفاقی در فولدر images افتاد در آن فولدر هم رخ دهد و ما دیتا را در یک جای مطمئن داشته باشیم. در مورد دیتابیس ها نیز به همین شکل عمل میکنیم و فایل های db را به کمک volume یا bind mount به یک محیط خارجی مپ میکنیم. در تصویر بالا volume و bind mount  را می‌بینید، یک حالتِ tmpfs هم داریم که اطلاعات را در memory نگه میدارد و دسترسی به آن سریع است، اما به لحاظ پایداری و حجم دیتا باید ملاحظاتی را در نظر بگیریم که در این مقاله فرصت پرداختن به آن نیست.در فایل docker compose اگر قرار باشد از حالت docker volume استفاده کنیم یک سکشن به نام Volume در سطح اول آن تعریف میکنیم و اسم ولیوم هایی که میخواهیم بسازیم را مشخص میکنیم. حالا در سکشن مربوط به دیتابیس این docker volume ها را به آدرس های داخل کانتینر مپ میکنیم. در mongo  محل ذخیره سازی اطلاعات دیتابیس و کانفیگ ها فولدرهایی هستند که میبینید:یک نمونه از فایل docker-composeبرنامه داکر دسکتاپ هم volume ها را نشان میدهدبا دستور زیر هم میتوانیم لیست volume ها را ببنیمdocker volume lsاگر هم بخواهید به شکل bind mount عمل کنید یعنی یک فولدر روی دیسک را به فولدری داخل کانتینر مپ کنید از روش زیر عمل کنید.در این حالت دو فولدر در مسیرهای داده شده روی سیستم عاملِ Host  تعریف میکنیم(چون من روی ویندوز کدنویسی میکنم برای توسعه فعلا دو فولدر در درایو c معرفی کرده ام که در تصویر میبینید). به محض اجرای داکر کامپوز، این دو فولدر ساخته میشوند و محتوای فولدرهای متناظر داخل کانتینر درون این فولدرها ساخته میشود. دقت کنید که دیگر داکر، volume مشخص برای اینکار نمی سازد.(در داکر دسکتاپ بخش Volume اطلاعاتی از این فولدرها نمایش نمیدهد)اگر نوع ذخیره سازی شما از نوع docker volume است (و Bind mount نیست)، اگر میخواهید محتوای volume های درست شده روی ویندوز را ببینید از آدرس زیر در پنجره explorer استفاده کنید:\\wsl$\docker-desktop-data\version-pack-data\community\docker\volumes\بطور کل در هر دو حالت شما این مزیت را بدست آوردید که فایل های دیتابیس شما در محلی فیزیکی روی سیستم ذخیره شده که حالا یا به شکل docker volume است و یا به شکل یک فولدر قابل دسترسی(bind mount) است. هر اتفاق بدی برای کانتینر شما بیفتد دیتای شما از بین نخواهد رفت. هر بار کانتینر را از بین ببرید و دوباره بسازید میتوانید به این دیتا دسترسی داشته باشید به شرط آنکه در فایل docker-compose مسیر ها را درست تعریف کنید.برای اجرای docker-compose  ابتدا مطمئن میشویم کانتینر برای تست از روی ایمیج ها نداشته باشیم. بهتر است قبلیها را پاک کنیم.ابتدا لیست کانتینرها را چک کردیم. بعد با دستور force که با -f مشخص است کانتینرهای موجود را پاک کردیم. سپس ایمیج ها را لیست گرفتیم و در نهایت فقط یکی از ایمیج ها را پاک کردیم و به ایمیج mongo دست نزدیم.(نیازی به پاک کردن ایمیج چیزهایی که pull کردیم نیست چون با اجرای دوباره docker-compose اگر وجود نداشته باشند دوباره pull میشوند) حالا در فولدری که docker-compose وجود دارد دستور زیر را مینویسیمdocker-compose -f .\docker-compose.yml -f .\docker-compose.override.yml up -dهمانطور که میبینید بر اساس اطلاعات موجود در docker-compose ایمیج ها به container تبدیل میشوند و همه چیز اجرا میشود.همیطور با دستور زیر میتوانید سرویس ها را down کنید و کانتینرها ابتدا stop و سپسremove میشوند.در این بخش نه تنها توانستیم اولین میکروسرویس خود را بسازیم بلکه با ایجاد docker-compose هم آشنا شدیم. پیش از این گفتیم docker-compose مختص این میکروسرویس نیست، به همین دلیل آن را خارج از فولدر این پروژه قرار دادیم. در بخش های بعد ما میکروسرویس های دیگری خواهیم نوشت که وظایف مختلفی دارند اما همه آنها با تکمیل همین docker-compose به مرحله عملیاتی و اجرا خواهند رسید.کدهای مربوط به این بخش را در این ریپازیتوری از گیتهاب ببنید (کامیت پایانی تا اینجا &quot;add and modify docker compose&quot;)</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Sun, 09 Jan 2022 17:15:38 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش داکر و مفاهیم اولیه برای برنامه نویسان</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AF%D8%A7%DA%A9%D8%B1-%D9%88-%D9%85%D9%81%D8%A7%D9%87%DB%8C%D9%85-%D8%A7%D9%88%D9%84%DB%8C%D9%87-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3%D8%A7%D9%86-rrfv3qnozegn</link>
                <description>این نوشته تلاشی برای استفاده کاربردی از داکر است. مفاهیم و تعاریف مطرح شده دقیق نیستند. صرفا کاربرد و فهم سازوکار داکر مد نظر نگارنده بوده است. اگر برنامه نویس هستید و چیزی از داکر نمی دانید این نوشته نقطه شروع خوبی است. پیش نیاز این مقاله آشنایی با یک زبان برنامه نویسی یا یک فریم ورک است. من از node به عنوان مثال استفاده کردم (در صورتی که برنامه نویس دات نت هستم). حتی المقدور همه مطلب را کامل مطالعه کنید و بخش های node و mongo را رد نکنید، روند ایجاد فایل داکر برای برنامه ها و ابزارهای مختلف یکسان است.این نوشته پیشنیازی برای مجموعه مقالات آموزش میکروسرویس است.برای کار با داکر قبل از هر چیز مطمئن شوید داکر را نصب کرده اید. اگر سیستم عامل ویندوز دارید با نصب docker desktop  میتوانید بدون مشکل از داکر استفاده کنید. در ویندوز بعد از نصب داکر فضایی شبیه به تصویر زیر خواهید دید.رنگ سبزِ پایین پنجره یعنی داکر روی سیستم شما فعال است. در سمت چپ صفحه لیستی از سه عنصر اساسی داکر میبینید.ImagesContainers/appVolumeداکر چیست و چگونه کار میکند؟اگر قبلا با دیسک های نوری یا همان سی دی کار کرده اید این مثال را به دقت بخوانید. Imageها در حقیقت مانند image های iso هستند که از سی دی تهیه میکردیم.( اگر با این image ها هم آشنایی ندارید، فرض کنید کل محتوای CD را در یک فایل Zip کنیم. این فایل زیپ شده را image میگویند.)حالا اگر فایل زیپ را در memory باز کنید و فایل اجرایی آن را اجرا کنید در حقیقت Container دارید.(یعنی همان Mount کردن فایل iso روی یک درایو مجازی)فرصت پرداختن به جزئیات داکر نیست اما برای استفاده عملی از داکر،  لازم است توسعه دهنده حداقل هایی از طرز کار داکر بداند. به تصویر زیر دقت کنید.تصویر فوق مراحل داکرایز کردن و ایجاد کانتینر را نشان میدهد. با نصب داکر روی سیستم، بصورت بالقوه قابلیت ایجاد فایل image داکر و اجرایی کردن آن و تبدیل آن به container را دارید. روال به صورت تصویر فوق است. کافیست در فولدر build پروژه تان، فایل dockerfile را به همین نام ایجاد کنید. این فایل حاوی دستورات مرتبط با نصب و اجرای پروژه است. بطور مثال برای پروژه های Node شما برای debug دستور nodemon یا node و سپس run را مینویسید. (اگر دات نتی هستید اینکارها شبیه dotnet run است و اگر با فریم ورک های دیگر کار میکنید دستورات اجرای پروژه مد نظر است). پس در داکر فایل، مجموعه دستوراتی برای اجرای پروژه قرار دارد به نحوی که شما را از نصب ملزومات به شکل دستی بی نیاز کند. مثلا اگر برای node یا dotnet نیاز به runtime دارید ابتدای فایل داکر باید به کمک یک دستور، پکیج مورد نظر را از اینترنت دریافت کنید تا ایمیج همراه با ملزومات ساخته شود. مزیت اصلی داکر همین است که یک image بطور مستقل و بدون نیاز به هیچ برنامه ای اجرا میشود. مثلا یک برنامه java یا python از دوستتان گرفته اید بدون اینکه runtime جاوا یا پایتون داشته باشید این برنامه اجرا نخواهد شد و مجبورید پیش از استفاده از برنامه، کتابخانه ها یا sdk ی مورد نیاز (حتا با ورژن مشابه روی سیستم دوستتان!) نصب کنید. داکر کردن یک برنامه همه ی ملزومات را به آن میچسباند و بی نیاز از کارِ اضافه خواهید بود، به علاوه اینکه دردسر همخوانی ورژن مورد نیاز برنامه با ورژن نصب شده روی سیستم را ندارید. اکثر اوقات محل قرارگیری «داکرفایل» به جای فولدر build، کنار فایل های پروژه است. این بستگی به رفتار توسعه دهنده دارد. وقتی dockerfile را در فولدر فایل های پروژه قرار میدهیم باید چیزهای دیگری هم در این فایل بنویسیم، چون در این صورت این فایل به غیر از مسئولیت اجرا، مسئولیت build هم به عهده دارد. یعنی ما ابتدا باید پکیج های ابزار مورد نیاز برای build مثل sdk را در ابتدای فایل داکر از اینترنت فراخوانی کنیم تا build درست انجام شود و سپس دستورات اجرایی را بنویسیم.(فایل داکر یک فایل دستورالعمل است)نمونه یک «داکرفایل» برای یک پروژه node را میبینید(برای فریم ورک ها و زبان های دیگر مشابه همین دستورالعمل است): https://gist.github.com/375121a02f4a835358d96e368f29552b در خط اول ما node را که ابزار لازم برای بیلد و اجرای پروژه است دریافت کردیم.(اگر دات نت یا پایتونی باشید باید ابزار مربوط به همان فریم ورک را دریافت کنید)(سپس مسیری که در آن پروژه قرار میگیرد را مشخص کردیم.سپس فایل package.json را کپی میکنیم.در خط بعد پکیج ها را با npm iنصب میکنیم.در مرحله بعد سورس کد را کپی میکنیم.یک پورت به شکل اسمی expose میکنیم.دستور اجرای پروژه را مینویسیم. (پروژه با پورتی که node خودش expose کرده اجرا میشود)بر اساس این دستورات به کمک دستور docker build یک ایمیج از فولدر پروژه ما ساخته میشود. این ایمیج به محض اینکه با دستور docker run به container تبدیل شد اجرا میشود یعنی پورتی که از داخل برای localhost تعریف شده بود میتواند به بیرون expose شود و از بیرون هم صدا زده شود.یک image وقتی به container تبدیل میشود در حقیقت در یک بستر کوچک لینوکس پروژه شما اجرا میشود. شاید اگر با داکر کار نکرده باشید درک این موضوع سخت باشد. اینطور تصور کنید که هر ایمیجی که به container تبدیل میشود انگار یک لینوکس راه اندازی میشود و دنیای خودش را دارد یعنی شبکه ها، پورت ها و فهرست فایل ها و ... .فرض کنید پروژه ای برای دانلود و آپلود فایل ساخته ایم.  فایل dockerfile را ساخته ایم. بر اساس این داکر فایل، با دستورات docker ابتدا از آن یک image ساخته و سپس با دستوراتی دیگر آن image را به container با خروجی 4200 تبدیل کرده ایم. یعنی بستر لینوکس مربوط به این پروژه را روشن و سپس خود پروژه را اجرا کرده ایم و گفته ایم localhost:4200 آن دسترسی به api های کد ما را میدهد. کانتینر به راحتی قابل توقف است. با نوشتن یک دستور کوچک میتوانیم آن را خاموش کنیم! و دیگر دسترسی به 4200 برقرار نیست. حتا میتوانیم آن را حذف کنیم و یا دوباره از روی ایمیج بسازیم. این یعنی ساختار فایل های لینوکس پاک شده و دوباره ساخته میشوند. مشکل اینجاست که چون پروژه ما در بستر همین ساختار فایل ایجاد شده، پس هر api در پروژه با همین ساختار فایل کار میکند. یعنی با فراخوانی api آپلود، فایل ها درون container آپلود میشوند. پس اگر روزی کانتینر متوقف و پاک شود و بخواهیم دوباره از ایمیج، کانتینر بسازیم فایل ها را از دست میدهیم. اینجا Volume مطرح میشود. یعنی ما یک فولدر خارج از container (محل فیزیکی روی سیستم) را به یک فولدر داخل container نسبت میدهیم. به همین راحتی. پس هر وقت container در حال اجرا بود و api آپلود یا دانلود فراخوانی شد ، فایل از/به داخل لینوکسِ کانتینر منتقل نمیشود. فایل به محلی فیزیکی و موجود روی کامپیوتری که این داکر روی آن نصب شده منتقل میشود.تصویر فوق همین موضوع را نشان میدهد. Docker host سیستمی است که ما داکر را روی آن نصب کردیم.(مثلا یک سیستم لینوکسی داریم و نه ویندوزی) ما روی این سیستم دو ایمیج داریم که به دو کانتینر تبدیل کرده ایم. حالا این کانتینرها به جای اینکه با file storage غیرمطمئن داخل خودشان کار کنند با فولدری فیزیکی و موجود از سیستم واقعی کار میکنند. به همین سادگی.چگونه از image ها استفاده کنیم؟همانطور که ما از پروژه های خود image تهیه میکنید در اینترنت ایمیج های رسمی و غیر رسمی از برنامه های مختلف وجود دارد. مثلا برای استفاده از sql server یا Mongo یا RabbitMq و ... نیازی به نصب مستقیم آن روی سیستم ندارید. مخزن داکر نسخه های رسمی از ایمیج های این برنامه ها را دارد و فقط کافیست آن را روی سیستم خودتان کپی یا pull  کنید. مثلا برای استفاده از Mongo کافیست روی سیستم خودتان بنویسید:docker pull mongoبا این دستور ایمیج به سیستم شما منتقل میشود و میتوانید آن را به container تبدیل کنید و با پورتی که خودتان تعریف کرده اید در کدهایتان از آن استفاده کنید.با یک مثال این موضوع را نشان میدهیم.فرض کنید میخواهید Mongo را روی سیستمان اجرا و استفاده کنید.کافیست در گوگل سرچ کنید و از سایت docker ایمیج رسمی آن را دریافت کنیداطلاعات مربوط به مونگو و نصب آن و env variable ها و کانفیگ ها در این صفحه وجود دارد.مراحل نصب را شروع میکنیم :1. ابتدا می بینیم چه image هایی روی سیستم داریم :docker imagesروی سیستم من دو ایمیج وجود داشت. اگر تازه docker desktop را نصب کرده باشید ممکن است هیچ ایمیجی روی سیستم شما نباشد.2. در ترمینال دستور docker pull mongo را بنویسید تا ایمیج به سیستم شما منتقل شود.docker pull mongo3. حالا ایمیج را به container تبدیل میکنیم یا به تعبیری اجرا میکنیم:docker run -d -p 27017:27017 --name shopping-mongo mongoاز d- برای دیتچ کردن اجرا و اجرای پشت صحنه استفاده میشود.(یعنی بعد از اجرا منتظر نماند و کنسول روی اجرا قفل نشود)از  p- برای مشخص کردن پورتی که به بیرون expose میکنیم استفاده میشود. پورت دیفالت 27017  در کانتینر برای مونگو استفاده میشود ما همین را اکسپوز میکنیم یعنی از بیرون با همین پورت به آن دسترسی داریم. ما پورت داخل کانتینر را به پورت لوکال کامپیوتر Expose کردیم.-p [docker host outside(local port)]:[container expose(image port)]از name-- برای تعیین نام برای کانتینری که از این ایمیج ساخته میشود استفاده میکنیم.در نهایت نام imageی که از روی آن کانتینر میسازیم را نوشتیم (mongo) 3. با دستور زیر چک میکنیم چه کانتینرهایی روی سیستم ما در حال اجرا هستند.docker psاین دستور ایمیج های کانتینر شده و در حال اجرا را نشان میدهد. و طبق تصویر فوق  21 ثانیه پیش شروع به کار کرده و پورت داخلی 27017 به پورت 27017خارجی مپ کرده است و نامش هم catalog-mongo است. همچنین شناسه آن b4b243f22fe6 است که در سیستم شما این شناسه متفاوت است.4. با دستور زیر میتوانیم لاگ این کانتینر را ببینیمdocker logs -f catalog-mongoو حتا با دستور زیر میتوانیم داخل ترمینال لینوکسی شویم که مونگو در بستر آن ایجاد شده و با دستورات ترمینال لینوکس در فولدرها بچرخیم یا پکیج نصب کنیم و ... .docker exec -it catalog-mongo /bin/bashداخل ترمینال لینوکس ، دستور mongo را اجرا میکنیم.حالا در ترمینال داخلی عبارت mongo را مینویسیم و میبینیم که شِل مونگو اجرا میشود و میتوانیم با دستور زیر از وجود دیتابیس ها مطمئن شویمبا فشردن Ctrl+C از محیط شل داخلی خارج میشویم و سپس با تایپ exit از محیط bash لینوکس خارج شوید.چون پورت را expose کرده ایم میتوانیم یک GUI مثل Compass یا studio3t  نصب کنیم و به پورت 27017 متصل کنید و از دیتابیس استفاده کنیم.برای توقف و حذف این کانتینر از دستور docker stop b4b243f22fe6  و docker rm b4b243f22fe6 استفاده کنید. دقت کنید ابتدا باید کانتینر را stop و سپس حذف کنید.(میتوانید با کمک آپشن f یا force یکباره آن را حذف کنید) با اینکار کانینر را از دست میدهید(در اینجا برای مانگو یعنی دیتابیس هایتان را از دست میدهید) ولی ایمیج هنوز باقیست و میتوانید دوباره با دستور docker run ایمیج را به کانتینر تبدیل کنید.دقت کنید که ما برای mongo مثال زدیم ولی شما میتوانید هر اپلیکیشنی که در مخزن داکر به عنوان یک image وجود دارد را به سیستم خودتان منتقل کنید و از شر فرایندهای نصب خلاص شوید.دوباره به تصویر اول این مقاله یعنی محیط docker desktop برگردید. شما میتوانید کارهایی را که در ترمینال با دستورات docker انجام دادید در محیط ویندوزی برنامه هم انجام دهید.اگر دوست دارید یک پروژه واقعی را داکرایز کنید و از مفهوم volume و network در داکر سردر بیاورید این مقاله را ببینید. (این مقاله در دست تهیه است)</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Sun, 02 Jan 2022 13:14:20 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش Node و Typescript برای تولید api</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-node-js-%D9%88-%D8%A2%D9%85%D9%88%D8%B2%D8%B4-typescript-g6qtblcdtetp</link>
                <description>برای دیدن ویدیوهای من در مورد برنامه نویسی عضو این کانال شوید :    https://t.me/mediapub_channelمیخواهیم یک تمپلیت ساده برای پروژه  Node به منظور تولید api بسازیم و به جای جاوااسکریپت از تایپ اسکریپت(Typescript) استفاده کنیم. اگر علاقه ای به تایپ اسکریپت ندارید (که بعید است) میتوانید همین پروژه را با صرف نظر از بخش های مرتبط با تایپ اسکریپت با جاوااسکریپت پیاده سازی کنید و تغییر چندانی در روند کدنویسی نخواهید داشت. پس مراحل موجود در همین مقاله را پیگیری کنید. تایپ اسکریپت به شما کمک میکند که روند توسعه ی سریع تر و پیشرفته تری داشته باشید.اینجا فرصت پرداختن به مزیت های تایپ اسکریپت نیست ولی همینقدر بدانید که برای من که توسعه دهنده دات نت هستم، جاوااسکریپت با تایپ اسکریپت شبیه به سی شارپ شد.(خالق این دو یک نفر است).تایپ اسکریپت یک پوشش کمک کننده در روند کدنویسی است. در نهایت شما کدهایتان را به کمک کامپایلر تایپ اسکریپت به جاوااسکریپت تبدیل میکنید. پس به لحاظ سرعت و پرفورمنس چیزی از دست نخواهید داد.در این نوشته :فرض شده که شما میدانید جاوااسکریپت و Node js چه هستند و Node را روی سیستم نصب کرده اید.فرض شده که ویژوال کد(Visual Code) را روی سیستم نصب دارید. فرض شده مفهوم Environment Variables را میدانید.فرض شده که مفهوم Api و Http Request را میدانید.در ادامه خواهید دید که برای ایجاد این تمپلیت کارهای عجیب نکرده ایم. مثلا از دکوریتورها (decorator) برای راحت تر کردن شکل دسترسی ها استفاده نکرده ایم. روتینگ(routing) را پیچیده نکرده ایم. میدل ویرهای(middleware) عجیب ننوشته ایم. معماری Clean Architecture را بطور کامل پیاده سازی نکرده ایم.(برای درک بیشتر این معماری این مقاله را بخوانید) این یک پروژه ساده و «کار راه انداز» است و به درد میکروسرویس هایی که وظایف کوچک دارند میخورد. (نمیدانید میکروسرویس چیست؟ این مقاله را بخوانید)مراحل زیر را به ترتیب طی می کنیم.1. ایجاد فولدر پروژه : فولدری به نام EcommerceMicroservics میسازیم و فولدر Catalog.Api را داخل آن ایجاد میکنیم. این فولدر را در ویژوال کد باز میکنیم. (با دستور . Code) حالا ترمینال ویژوال کد را باز میکنیم و ادامه میدهیم (با شورتکات ctrl+&#x60; )2. اطمینان از نصب Node : مطمئن میشویم node و npm را روی سیستم داریم. برای اینکار داخل ترمینال Visual Code عبارت node --version و npm --version مینویسیم.3. ایجاد پروژه Node : با دستور npm init پروژه درست میکنیم و سوالات را به شکل زیر جواب میدهیم.(فلش های سبز را جواب میدهیم، بقیه برای شروع کار اهمیتی ندارند) چون میخواهیم از تایپ اسکریپت      استفاده کنیم.فایل استارتاپ را به نام server.ts مشخص میکنیم.در نهایت یک فایل package.json با محتوای اطلاعات وارد شده ایجاد میشود.4. نصب کتابخانه های مربوط به زمان توسعه : کتابخانه های زیر را نصب میکنیمnpm i -g typescript nodemon ts-node prettiernodemon : ابزاری برای واچ کردن تاثیر تغییرات روی کد در حین اجرا و دیباگ استTs-node : ابزاری برای ایجاد محیط تایپ اسکریپت در توسعه نود استPrettier : مرتب کننده کددقت کنید که به شکل گلوبالی نصب میکنیم(آپشن g-) چون میتوانیم به شکل جدا و مستقل در هر پروژه ای آنها را فراخوانی کنیم.5. نصب اکستنشن روی ویژوال کد : مطمئن میشویم اکستنشن Prettier روی ویژوال کد نصب باشد تا کدهای ما با فرمت مناسب مرتب سازی شوند.6. ایجاد فایل تنظیمات تایپ اسکریپت : دستور زیر را برای ایجاد فایل تنظیمات تایپ اسکریپت اجرا میکنیم.tsc --initبا اینکار tsconfig.json ایجاد میشود. در این فایل پراپرتی outDir برای تنظیم فولدر فایل های جاوااسکریپت تولید شده توسط کامپایلر تایپ اسکریپت تعریف میشود. مقدارش را مثل شکل زیر build قرار میدهیم.7. اضافه کردن اسکریپت بیلد : حالا روش بیلد شدن را در package.json مشخص میکنیم.چند دستور نوشتیم که با &amp;&amp; جدا کردیم.اولین دستور برای پاک کردن محتوای قدیم فولدر build است. دومین دستور برای مرتب سازی کدهای موجود در فولدر source است و آخرین دستور یعنی tsc فایل های ts را کامپایل کرده و js تولید میکند.در ویندوز برای ایجاد و حذف فولدر، به کمک npm دو کتابخانه rimraf و mkdir را نصب کنید و خط build را به شکل زیر بنویسید.&amp;quotbuild&amp;quot: &amp;quotrimraf ./build &amp;&amp; prettier --write source/ &amp;&amp; tsc&amp;quotبه کمک دستور npm run build آنچه در پراپرتی build داخل پراپرتی scripts از فایل package.json تعریف کرده ایم اجرا میشود.8. نصب کتابخانه های مربوط به تولید api : پکیج های مورد نیاز دیگر را نصب میکنیم. (اینبار گلوبالی نیست و برای همین پروژه است)npm i express body-parser dotenvاکسپرس : کتابخانه ای که ابزار لازم برای ایجاد api را به ما میدهد.بادی پارسر : مفسری برای تشخیص دیتای ارسال شده به شکل ریکوئست است.دات اِنو : دسترسی به متغیرهای environment  را به ما میدهد9. نصب type definition های کتابخانه ها : باید کتابخانه ای برای ایجاد اینترفیس های قابل فهم توسط تایپ اسکریپت به ازای هر پکیج مرتبط با توسعه ی کد، نصب کنیم.npm i @types/express @types/body-parser @types/dotenv10. تنظیمات اکستنشن prettier : از روی داکیومنت مربوط به prettier باید تنظیمات  آن را در یک فایل به نام .prettierrc ایجاد کنیم https://gist.github.com/48cf9aeb20f84cc127a8a59ccff8d0b2 11.تنظیمات پروژه : تظیمات مربوط به پروژه را در فایل setting.json به شکل زیر ایجاد میکنیم. تنظیمات مربوط به اکستنشن prettier و رفتار ویژوال کد با پروژه ماست. https://gist.github.com/9c82a69f01fb414be1c3ffca4d61c4f5 ایجاد فایل های مورد نیاز پروژهفایلی برای تنظیمات اولیه پروژه ایجاد میکنیم که مثلا روی چه ip و پورتی هاست شود. این فایل شروطی دارد که اگر environment variable ما متغیرهای مورد نظر را نداشت از مقادیر پیش فرض استفاده کند. این فایل را در فولدر source/configs به نام config.ts ایجاد میکنیم. https://gist.github.com/30ee64d7da37424d5fc418f50fa7e046 2. فایلی برای متدهای لاگ تعریف میکنیم که فرمت دلخواه و مناسب پروژه شما را باید داشته باشد. این فایل را در مسیر source/configs/log.ts ایجاد میکنیم. https://gist.github.com/eac118248b42b078c1691110f3510e33 3. فایل شروع کننده را به نام server.ts میسازیم. این فایل نیاز به توضیحاتی دارد که در داخل کد کامنت شده است. https://gist.github.com/f16069624cbb608a7ba602f2ce673424 4. برای اجرای پروژه از دستور زیر استفاده میکنیم. با این روش هر بار تغییری روی پروژه صورت گیرد کل پروژه دوباره اجرا میشود.nodemon source/server.ts5. در مرورگر یا پستمن localhost:4300 را صدا میزنیم. باید پیامی که بعنوان خطا در میدل ویر تعریف کردیم برگرداند چون هیچ روت دیگری ندارد. در ادامه روت و متد های کنترلر را ایجاد میکنیم. ابتدا در ترمینال ویژوال کد با Ctrl+C اجرای پروژه را متوقف کنید.6.یک متد ساده بعنوان تست در مسیر source/controllers/test.ts ایجاد میکنیم که مقداری را برگرداند و آن را به یک روت (Route) متصل میکنیم. https://gist.github.com/d756e81282607c474e5cebd6125be9e3 7.یک متد ساده برای تعیین Route در مسیر source/routes/test.ts ایجاد میکنیم که روت ها را به متدهای شماره 5 مپ کند. https://gist.github.com/76783d8ff1927715ae4403bb9d6d92e5 8. با دستور nodemon source/ts پروژه را اجرا میکنیم و در مرورگر یا پستمن آدرس localhost:4300/test/healtcheck را صدا میزنیم. باید عبارت health check Ok! را ببینید.9. برای بیلد کردن دستور زیر را مینویسیم و فولدر بیلد ساخته میشودnpm run build همانطور که در تصویر میبینید این فولدر حاوی فایل های js است و قابل انتقال به سرور برای اجرا است. برای اجرای این فولدر کافیست از دستور node server.js استفاده کنید.دیباگ در ویژوال کدبرای دیباگ و قراردادن برک پوینت (Break Point) روی خطوط، باید یک فایل launch.json با تنظیمات زیر بسازید. برای اینکار از منوی Run گزینه Add Configuration را انتخاب کنید و کد زیر را بنویسید. https://gist.github.com/07e25d8b73cb011dcc568d98a5938a85 و حتما در فایل tsconfig.json تغییرات زیر را اعمال کنید. https://gist.github.com/513a19dbaf01b16f8f71f401a67768a6 اگر فایل شما به این شکل است، پراپرتی ها را پیدا کنید و از کامنت خارج کنید.حالا به جای نوشتن دستور nodemon با F5 میتوانید پروژه را دیباگ کنید.اگر علاقمند هستید این پروژه را با یک crud روی دیتابیس mongo تکمیل و در نهایت داکرایز کنید، این مقاله را بخوانید.</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Sat, 01 Jan 2022 08:40:47 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش میکروسرویس Microservice - شروع پیاده سازی یک پروژه فروشگاهی (بخش چهارم)</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D9%85%DB%8C%DA%A9%D8%B1%D9%88%D8%B3%D8%B1%D9%88%DB%8C%D8%B3-microservice-%D8%B4%D8%B1%D9%88%D8%B9-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%DB%8C%DA%A9-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D9%81%D8%B1%D9%88%D8%B4%DA%AF%D8%A7%D9%87%DB%8C-%D8%A8%D8%AE%D8%B4-%DA%86%D9%87%D8%A7%D8%B1%D9%85-brnugjuwsiei</link>
                <description>بخش اول : آشنایی با مفهوم میکروسرویسبخش دوم : ویژگی های اصلی یک میکروسرویسبخش سوم : تحلیل یک پروژه کوچک بر اساس میکروسرویس هابخش چهارم : شروع پیاده سازی یک پروژه فروشگاهی  (همین مقاله)پیشنیاز 1 بخش پنجم : آموزش Node و Typescript برای تولید apiپیشنیاز 2 بخش پنجم : آموزش داکر و مفاهیم اولیهبخش پنجم : پیاده سازی یک میکروسرویس برای نمایش کالاهاپیشنیاز 1 بخش ششم : آشنایی با Asp.net core 6 بخش ششم : پیاده سازی یک میکروسرویس برای کار با سبد خریدبخش هفتم : پیاده سازی یک میکروسرویس برای محاسبه قیمت و تخفیفاتبخش هشتم: پیاده سازی یک میکروسرویس برای ثبت سفارشبرای دیدن ویدیوهای من در مورد برنامه نویسی عضو این کانال شوید :    https://t.me/mediapub_channelطبیعتا در این نوشته و نوشته های بعدی فرصت پرداختن به شکل کد نویسی یا رعایت اصول برنامه نویسی و معماری Clean نیست. هدف پیاده سازی بخشی از یک پروژه فروشگاهی است که بتوانیم به کمک آن از معماری میکروسرویس سر در بیاوریم. مثال پیش رو با هدفی کاملا آموزشی تهیه شده است و در طی چندین مقاله به پیاده سازی آن خواهیم پرداخت. از حاشیه و یا مقدمات برنامه نویسی در روند نگارش این مقالات خودداری کردم که سریعتر و جامعتر تنها به اصول طراحی میکروسرویس ها پرداخته شود.یک سیستم ثبت کالا در سبد و ثبت سفارشقرار است چهار پروژه میکروسرویس تهیه کنیم که هر کدام دیتابیس خودش را دارد.از دیتابیس و زبان های برنامه نویسی متنوع استفاده میکنیم تا کاملا نسبت به مستقل بودن این معماری از ابزار کدنویسی مطمئن شوید. در ادامه به شرح این سیستم می پردازیم.سیستم فوق به وسیله میکروسرویس Catalog.Api اطلاعات مربوط به کالا را ذخیره و تغییر و نمایش  می دهد. دیتابیس این میکروسرویس Mongo است. فرض کنید به این دلیل این انتخاب صورت گرفته که اولا حجم اطلاعات زیادی داریم و ثانیا ساختار کاملا مشخصی برای استراکچر هر کالا نداریم یا اگر هم داریم ممکن است در آینده تغییر کند. نکاتی مثل کوئری های سریعتر و انعطاف پذیری هم در کنار این مزایا بگذارید. تصمیم داریم به دلیل در اختیار داشتن یک تیم برنامه نویسی مسلط به Javascript از Node برای پیاده سازی این میکروسرویس استفاده کنیم. برای درست کردن این میکروسرویس سه مرحله در پیش داریم. اول آماده سازی دیتابیس دوم آماده سازی api ها به کمک Node و سوم داکرایز کردن پروژه و ایجاد ایمیج(image) و به تبع آن کانتینر(container). این سه مرحله در سه میکروسرویس بعدی نیز باید رعایت شود.اگر نمیدانید داکر چیست چیز خاصی از دست نداده اید. اگر در قدیم با سی دی(صفحه نوری) کار کرده باشید امکانی وجود داشت که از سی دی ایمیج بگیریم و فایل ISO بسازیم. در سیستم عامل ها این امکان وجود دارد که ایمیج یک CD را به شکل یک درایو مجازی Mount کنیم یا به تعبیری اجرا کنیم. همین روند را میتوان با پروژه هم انجام داد. یعنی پروژه معادل یک سی دی است. به کمک داکر از پروژه یک ایمیج میگیریم که حاوی اطلاعات برنامه (فایل اجرایی و ملحقات) و کتابخانه های مورد نیاز است. این ایمیج روی هر سیستمی بدون نیاز به نصب فریم ورک یا کتابخانه خاصی، قابل Mount شدن است به شرط آنکه داکر روی آن سیستم نصب باشد. این کار را کانتینرایز کردن میگویند. به تعبیری انگار image را اجرا کرده ایم. این اجرا روی مثلا یک آی پی و پورت خاص صورت میگیرد.خروجی عینا شبیه npm start در نود یا dotnet run در دات نت یا flask run در فلاسک خواهد بود و پروژه شما روی آی پی و پورت مشخصی اجرا خواهد شد.نگران نباشید، قبل از این بخش این مقاله را بخوانید. همچنین حین پیاده سازی هر میکروسرویس با روند داکرایز کردن آن آشنا خواهید شد.میکروسرویس بعدی Basket.Api است. وظیفه این سرویس ذخیره کردن سبد کالای کاربر است. محل نگهداری دیتابیسِ سریع و موقت (و انقلابی!) Redis است که Data Store اختصاصی همین میکروسرویس است. ما دات نت را برای توسعه این میکروسرویس انتخاب کرده ایم. در بخش مربوط به این میکروسرویس در مورد مزایای انتخاب Redis و ویژگی های دات نت صحبت خواهیم کرد. همچنین برای نهایی کردن سبد خرید از RabbitMQ استفاده میکنیم و اطلاعات سبد را برای ثبت سفارش به میکروسرویس Ordering.Api ارسال میکنیم. در این روش میکروسرویس Ordering.Api به event مرتبط با این عملیات گوش میکند (Subscribe میشود) و هر موقع رویداد نهایی کردن سبد رخ داد، عملیات ثبت سفارش را انجام میدهد. این عملیات در بک گراند اتفاق می افتد و به شکل Asynchronous انجام میشود. در مورد علت و روش این مکانیزم در بخش مربوطه توضیح خواهم داد. همچنین این میکروسرویس به کمک gRPC با میکروسرویس Discount.Api ارتباط داخلی دارد. در مورد gRPC و علت کاربرد آن در این محل، بعدا توضیح خواهم داد.میکروسرویس بعدی Discount.Api است که وظیفه در اختیار قرار دادن اطلاعات تخفیف و قیمت کالا را به عهده دارد. دات نت را برای این بخش انتخاب کرده ایم. به کمک روش gRPC از طریق Basket.Api یک درخواست داخلی به این میکروسرویس ارسال میکنیم و اطلاعات سبد را میدهیم تا این میکروسرویس بتواند قیمت اقلام و قیمت نهایی را محاسبه کند و ریسپانس مناسب تولید کند. gRPC و تفاوت آن با Http Request معمولی در بخش مرتبط با این میکروسرویس توضیح داده خواهد شد. همچنین اطلاعات در این بخش در یک دیتابیش PostgreSql ذخیره میشود. میکروسرویس بعدی Ordering.Api است که با زبان برنامه نویسی پایتون و فریم ورک Flask پیاده سازی خواهیم کرد. دیتابیسی که برای این بخش انتخاب کرده ایم Sql Server است. این بخش یک شنونده برای RabbitMQ است و بر اساس اتفاقاتی که در event موجود در سرویس Basket.Api می افتد تغییراتی به دیتابیس اعمال میکند. همچنین این میکروسرویس مسئول تولید گزارش و لیست هایی از سفارشات ثبت شده در دیتابیس خودش است.همچنین مدیریت پراکسی برای این پروژه به عهده Ocelot است که به موقع با آن و مکانیزم aggregate آشنا میشویم. ما از بیرون فقط به سرویس هایی که این Gateway به بیرون اعلام کرده دسترسی داریم.تمامی این مجموعه به کمک docker نگهداری میشود. ما از docker-compose برای مدیریت این سرویس ها استفاده خواهیم کرد.اگر در مورد هر کدام از دیتابیس ها یا RabbitMQ یا gRPC یا ocelot Gateway اطلاعاتی ندارید نگران نباشید. در هر بخش به تفصیل به ابزاری که استفاده میکنیم خواهیم پرداخت.در بخش بعد با اولین میکروسرویس یعنی Catalog.Api شروع میکنیم.</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Tue, 21 Dec 2021 10:01:17 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش میکروسرویس Microservice - تحلیل یک پروژه کوچک بر اساس میکروسرویس ها (بخش سوم)</title>
                <link>https://virgool.io/@mortezadalil/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D9%85%DB%8C%DA%A9%D8%B1%D9%88%D8%B3%D8%B1%D9%88%DB%8C%D8%B3-microservice-%D8%AA%D8%AD%D9%84%DB%8C%D9%84-%DB%8C%DA%A9-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%DA%A9%D9%88%DA%86%DA%A9-%D8%A8%D8%B1-%D8%A7%D8%B3%D8%A7%D8%B3-%D9%85%DB%8C%DA%A9%D8%B1%D9%88%D8%B3%D8%B1%D9%88%DB%8C%D8%B3-%D9%87%D8%A7-%D8%A8%D8%AE%D8%B4-%D8%B3%D9%88%D9%85-z1x7mfcfu9oo</link>
                <description>بخش اول : آشنایی با مفهوم میکروسرویس بخش دوم : ویژگی های اصلی یک میکروسرویسبخش سوم : تحلیل یک پروژه کوچک بر اساس میکروسرویس ها (همین مقاله)بخش چهارم : شروع پیاده سازی یک پروژه فروشگاهیپیشنیاز 1 بخش پنجم : آموزش Node و Typescript برای تولید apiپیشنیاز 2 بخش پنجم : آموزش داکر و مفاهیم اولیهبخش پنجم : پیاده سازی یک میکروسرویس برای نمایش کالاهاپیشنیاز 1 بخش ششم : آشنایی با Asp.net core 6 بخش ششم : پیاده سازی یک میکروسرویس برای کار با سبد خریدبخش هفتم : پیاده سازی یک میکروسرویس برای محاسبه قیمت و تخفیفاتبخش هشتم: پیاده سازی یک میکروسرویس برای ثبت سفارشبرای دیدن ویدیوهای من در مورد برنامه نویسی عضو این کانال شوید :    https://t.me/mediapub_channelتا اینجا در دو مقاله قبل، بدون ورود به جزئیات، کمی از فلسفه و چرایی وجود میکروسرویس ها صحبت کردیم. در این مقاله می خواهیم به عنوان نمونه یک سیستم نسبتا ساده را تحلیل کنیم. پیش از تحلیل به تعریف این سیستم می پردازیم.تعریف : کاربر در یک فروشگاه اینترنتی با لیستی از کالاها در مرورگر وب روبرو است. کاربر کالا(یا کالاها) را انتخاب و به سبد خود می افزاید. در حین عملیات، سرور باید رفتار کاربر را رصد کند که چه صفحاتی را میبیند و کجاها کلیک میکند. همچنین سیستم باید بر اساس رفتار کاربر و انتخابهایش، پیشنهاداتی به وی برای خرید کالاهای جدید بدهد.قبل از شروع تحلیل، اگر چنین پروژه ای به شما واگذار شود قبل از هر چیز به چه موضوعی فکر میکنید؟ من به موضوع کمیت تیم برنامه نویسی فکر میکنم.تیم برنامه نویسی من چند نفره است؟به نظر من، مهمترین عاملی که شاید مانع پیاده سازی یک پروژه به شکل میکروسرویس شود یک یا دو نفره بودن تیم برنامه نویسی است. تکه تکه کردن یک سیستم به زیرسیستم های مستقل نیازمند نگهداری، تست و پابلیش مستقل است. زحمتی که تیم توسعه برای این موارد متحمل میشود گاهی ارزش پیاده سازی به روش میکروسرویس را تحت تاثیر قرار میدهد. ضمنا مساله دیتابیس و شکل پابلیش ( استفاده از داکر و مدیریت کانتینرها با ابزاری مثل کوبرنتیز و ... ) را در نظر بگیرید.دقیقا مشابه این موارد را میتوان برای پیاده سازی Clean Architecture در یک پروژه کوچک تعمیم داد. چرا باید یک پروژه که از تعدادی api محدود تشکیل شده، بیزنس ساده ای دارد و توسط یک نفر توسعه می یابد و قرار نیست ماه ها و سال ها تغییر کند با معماری Clean و با دقت در تفکیک لایه ها انجام شود؟ پس قبل از هر چیز موضوع پروژه را سبک سنگین کنید.به عنوان مثال در نظر بگیرد که قرار است یک سایت شخصی طراحی کنم که محصولات آموزشی خود را در آن می فروشم. به دلایلی  مجبورم تا هفته آینده سایت را راه اندازی کنم. بسیاری از امکانات بعد از راه اندازی به سایت اضافه میشود. مثل پنل ادمین، صفحات جدید، مکانیزم کلاس آنلاین و امکانات اس ام اس و ... . با آگاهی از اینکه قرار است بعد از راه اندازی، هفتگی و یا حتا روزانه امکانات جدید به سایت اضافه کنم تصمیم گرفتم که : از یک فریم ورک یا لایبرری جدا مثل ری اکت یا انگولار برای UI استفاده نکنم و با Asp Core MVC پروژه را انجام دهم. که پابلیش یکپارچه داشته باشم و هر بار برای توسعه و تست مجبور به کار کردن با دو پروژه جدا و نگهداری و تست دو پروژه نباشم.معماری Clean Architecture و CQRS را پیاده سازی کنم چون سایت نیاز به توسعه دارد و امکان تغییر لایه ها و حتا دیتابیس در آینده وجود دارد. برای CQRS شک دارم، چون بعید است بخواهم Command ها و Query ها را در دو دیتابیس(یا روش) جدا و با ریپازیتوری جدا پیاده سازی کنم اما لحاظ کردن قواعد اولیه CQRS چندان زحمتی به من نمیدهد. (فوقش دو تا ریپازیتوری برای هر Entity میسازم)میکروسرویس به درد من نمیخورد چون با توجه به مورد 1 و اینکه خودم به تنهایی توسعه سایت را بر عهده دارم مزیتی برای شکستن پروژه به سرویس های کوچک نمیبینم. با توجه به تمامی موارد گفته شده و فرض اینکه میکروسرویس برای این سیستم بهترین روش است، تحلیل این مثال را شروع میکنم. نگاه از بالا به پروژهاز ساده ترین زاویه به پروژه نگاه میکنیم. تصویر فوق نشان میدهد که کاربر قرار است یک ریکوئست برای اضافه کردن آیتم به سبد خرید به سمت سرور ارسال کند. Gateway این ریکوئست را دریافت میکند و به سرویس(یا سرویس ها) میفرستد و نتیجه تولید شده از طریق همین Gateway به کاربر نمایش داده میشود.اتفاق های جالب در پشت API Gateway افتاده که توانسته است ریسپانس مناسب را تولید کند. برای به روزرسانی سبد و اضافه شدن کالای جدید به سبد، API Gateway از چند میکروسرویس استفاده میکند. هر میکروسرویس پروسه جدا دارد و ارتباطات در این مثال در بستر HTTP صورت میگیرد.جزئیات اتفاقات در تصویر زیر مشخص است :بخش API Gateway مسئول یک اعتبارسنجی اولیه از درخواست دریافتی است. به محض ولید شدن، کار ابتدا به میکروسرویس Shopping Cart و سپس به میکروسرویس Price Calculation سپرده میشود.میکروسرویس Shopping Cart از میکروسرویس دیگری به نام Product Catalog استفاده میکند تا اطلاعات لازم در مورد کالا را دریافت کند.      سپس این میکروسرویس اطلاعات سبد کالای کاربر را در دیتابیس خود ذخیره میکند،و این اطلاعات را به API Gateway هم ارسال میکند. به منظور پرفورمنس این میکروسرویس اطلاعات سبد کالای مشتری را (که از میکروسرویس Product Catalog دریافت کرده) Cache میکند. تا برای دفعات بعد از ارتباط بینِ میکروسرویسی بینیاز باشد.میکروسرویس Price Calculation وظیفه محاسبه قیمت و تخفیفات را برای تکمیل سبد کالای مشتری به عهده دارد.همانطور که میبینید هر میکروسرویس وظیفه منحصر بفرد و تخصصی خودش را در این روند ایفا میکند و از میکروسرویس های دیگر در حدی که نیاز دارد خبر دارد. به عنوان مثال میکروسرویس price calculation هیچ اطلاعاتی در مورد کالاهایی که در میکروسرویس product catalog ذخیره شده ندارد. این موضوع مفهوم اصلی میکروسرویس است که هر جزء مسئولیت منحصر به فرد خودش را دارد.تا اینجای کار سبد کالای کاربر تشکیل شده است. اما بررسی دو موضوع دیگر در صورت مساله خواسته شده است:موتور پیشنهاد دهنده که به عنوان میکروسرویس مستقل طراحی شده، مدل داخلی خود را آپدیت میکند. علاقه کاربر بر اساس آخرین اضافه شدن به سبد آپدیت میشود.سرویس tracking آیتمی که کاربر به سبد خود اضافه کرده را به دیتابیس tracking  اضافه میکند. این اطلاعات ممکن است بعدا برای گزارش گیری استفاده شود.این اتفاقات نیازی به بازگرداندن فیدبک به کاربر ندارند. یعنی میتوانند به طور موازی حین پاسخگویی به request انجام شوند. این نوع عملیات یا اکشن ها را میتوانیم به عنوان سایدافکت های یک درخواست در نظر بگیریم. سایدافکت ها تاثیر مستقیم روی ریکوئست کاربر ندارند. (ارسال ایمیل یا اس ام اس در صورتی که محتوای اطلاع رسانی[و نه اعتبارسنجی] داشته باشند میتوانند ساید افکت یک فرایند باشند)این اضافات را به Shopping cart microservice اضافه کنید.در تصویر فوق دیده میشود که یک رویداد (event) به موازات درخواست کاربر برای به روزرسانی سبد اتفاق خواهد افتاد. این رویداد به نام ItemAddedToCart است و توسط میکروسرویس Shopping cart اتفاق می افتد. دیگر میکروسرویس هایی که مسئولیت کارهای موازی را دارند یعنی Recommendations و Shopping Tracking باید عضو(Subscribe) این رویداد(event) شوند. در حقیقت این میکروسرویس ها شنونده هستند و هر موقع رویداد اتفاق بیفتد این دو سرویس شنونده خواهند بود و اطلاعات را دریافت کرده و وظایف خود را انجام خواهند داد.سابسکرایبر ها به کال شدن ایونت ری اکشن نشان میدهند. این ری اکشن خارج از کانتکست ریکوئست اصلی است، پس میتوان گفت که ساید افکت ها به شکل موازی با ریکوئست اصلی اتفاق می افتند و یا میتوانند بعد از ریسپانس ِاصلی ادامه داشته باشند. (واقعا فارسی نوشتن این پاراگرافِ مهم، جفا در حق میکروسرویس بود)تصویر کاملدر مجموع شش میکروسرویس در تصویر وجود دارند که یک ریکوئست را به ریسپانس تبدیل میکنند و یک کالا را به سبد کاربر اضافه میکنند. هیچ کدام از این میکروسرویس ها از فرایند داخلی دیگر سرویس ها خبر ندارند. پنج تا از این ها data store خودشان را دارند که تنها موظف به کاریست که در حوزه فعالیت میکروسرویس خودش تعریف شده است. اکثر این میکروسرویس ها به شکل synchronously در بستر ریکوئست کاربر عمل میکنند و بعضی هم به شکل asynchronously اتفاق می افتند(event ها).نمای کامل از فرایند ثبت کالا در سبد کاربردر بخش بعدی شروع به پیاده سازی مثال دیگری مشابه همین مثال خواهیم کرد.</description>
                <category>مرتضی دلیل</category>
                <author>مرتضی دلیل</author>
                <pubDate>Sun, 19 Dec 2021 10:06:42 +0330</pubDate>
            </item>
            </channel>
</rss>