بلاگر و تگهای BR اضافی!
تنظیمات، اصلی، انتخاب ویرایشگر پست ، گزینه «ویرایشگر به روز شده» را انتخاب کنید.
Dart کتابخانه ای است که توسط شرکت گوگل ارائه شده است و گفته میشود، قرار است جایگزین جاوا اسکریپت گردد و از آدرس https://www.dartlang.org قابل دسترسی میباشد. این کتابخانه، دارای انعطاف پذیری فوق العاده بالایی است و کد نویسی Java Script را راحتتر میکند. در حال حاضر هیچ مرورگری به غیر از Chromium از این تکنولوژی پشتیبانی نمیکند و جهت تسهیل در کدنویسی، باید از ویرایشگر Dart Editor استفاده کنید. این ویرایشگر کدهای نوشته شده را به دو صورت Native و JavaScript Compiled در اختیار مرورگر قرار میدهد. در ادامه با نحوهی کار و راه اندازی Dart آشنا خواهید شد.
ابتدا Dart و ویرایشگر مربوط به آن را توسط لینکهای زیر دانلود کنید:
دانلود نسخه 64 بیتی دارت + ویرایشگر
دانلود نسخه 32 بیتی دارت + ویرایشگر
بعد از اینکه فایلهای فوق را از حالت فشرده خارج کردید، پوشه ای با نام dart ایجاد مینماید. وارد پوشه dart شده و DartEditor را اجرا کنید.
توجه: جهت اجرای dart به JDK 6.0 یا بالاتر نیاز دارید
در مرحله بعد نمونه کدهای Dart را از لینک زیر دانلود نمایید و از حالت فشرده خارج کنید. پوشه ای با نام one-hour-codelab ایجاد میگردد.
از منوی File > Open Existing Folder… پوشه one-hour-codelab را باز کنید .
توضیحات
- پوشه packages و همچنین فایلهای pubspec.yaml و pubspec.lock شامل پیش نیازها و Package هایی هستند که جهت اجرای برنامههای تحت Dart مورد نیاز هستند. Dart Editor این نیازمندیها را به صورت خودکار نصب و تنظیم میکند.
توجه: اگر پوشه Packages را مشاهده نکردید و یا در سمت چپ فایلها علامت X قرمز رنگ وجود داشت، بدین معنی است که package ها به درستی نصب نشده اند. برای این منظور بر روی pubspec.yaml کلیک راست نموده و گزینه Get Pub را انتخاب کنید. توجه داشته باید که بدلیل تحریم ایران توسط گوگل باید از ابزارهای عبور از تحریم استفاده کنید.
- 6 پوشه را نیز در تصویر فوق مشاهده میکنید که نمونه کد piratebadge را بصورت مرحله به مرحله انجام داده و به پایان میرساند.
- Dart SDK شامل سورس کد مربوط به تمامی توابع، متغیرها و کلاس هایی است که توسط کیت توسعه نرم افزاری Dart ارائه شده است.
- Installed Packages شامل سورس کد مربوط به تمامی توابع، متغیرها و کلاسهای کتابخانههای اضافهتری است که Application به آنها وابسته است.
گام اول: اجرای یک برنامه کوچک
در این مرحله سورس کدهای آماده را مشاهده میکنید و با ساختار کدهای Dart و HTML آشنا میشوید و برنامه کوچکی را اجرا مینمایید.
در Dart Editor پوشه 1-blankbadge را باز کنید و فایلهای piratebadge.html و piratebadge.dart را مشاهده نمایید.
کد موجود در فایل piratebadge.html
<html> <head> <meta charset="utf-8"> <title>Pirate badge</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="piratebadge.css"> </head> <body> <h1>Pirate badge</h1> <div> TO DO: Put the UI widgets here. </div> <div> <div> Arrr! Me name is </div> <div> <span id="badgeName"> </span> </div> </div> <script type="application/dart" src="piratebadge.dart"></script> <script src="packages/browser/dart.js"></script> </body> </html>
توضیحات
- در کد HTML ، اولین تگ <script> ، فایل piratebadge.dart را جهت پیاده سازی دستورات dart به صفحه ضمیمه مینماید
- Dart Virtual Machine (Dart VM) کدهای Dart را بصورت Native یا بومی ماشین اجرا میکند. Dart VM کدهای خود را در Dartium که یک ویرایش ویژه از مرورگر Chromium میباشد اجرا میکند که میتواند برنامههای تحت Dart را بصورت Native اجرا کند.
- فایل packages/browser/dart.js پشتیبانی مرورگر از کد Native دارت را بررسی میکند و در صورت پشتیبانی، Dart VM را راه اندازی میکند و در غیر این صورت JavaScript کامپایل شده را بارگزاری مینماید.
کد موجود در piratebadge.dart
void main() { // Your app starts here. }
- این فایل شامل تابع main میباشد که تنها نقطه ورود به application است. تگ <script> موجود در piratebadge.html برنامه را با فراخوانی این تابع راه اندازی میکند.
- تابع main() یک تابع سطح بالا یا top-level میباشد.
- متغیرها و توابع top-level عناصری هستند که خارج از ساختار تعریف کلاس ایجاد میشوند.
جهت اجرای برنامه در Dart Editor بر روی piratebadge.html کلیک راست نمایید و گزینه Run in Dartium را اجرا کنید. این فایل توسط Dartium اجرا میشود و تابع main() را فراخوانی میکند و صفحه ای همانند شکل زیر را نمایش میدهد.
گام دوم: افزودن فیلد input
توجه داشته باشید که در این مرحله یا میتوانید تغییرات مورد نظر خود را در طی آموزش بر روی پوشهی 1-blankbadge اعمال کنید و یا به پوشههای تهیه شده در نمونه کد موجود در همین پروژه مراجعه نمایید.
در این مرحله یک تگ <input> به تگ <div class=”widgets”> اضافه کنید.
... <div> <div> <input type="text" id="inputName" maxlength="15"> </div> </div> ...
سپس کتابخانه dart:html را به ابتدای فایل piratebadge.dart اضافه کنید.
import 'dart:html';
توضیحات
- دستور فوق کلاسها و Resource های موجود در کتابخانه dart:html را اضافه میکند.
- از حجیم شدن کدهای خود نگران نباشید، زیرا فرایند کامپایل کدهای اضافی را حذف خواهد کرد.
- کتابخانه dart:html شامل کلاسهایی جهت کار با عناصر DOM و توابعی جهت دسترسی به این عناصر میباشد.
- در مباحث بعدی یاد میگیرید که با استفاده از کلمه کلیدی show فقط کلاسهایی را import کنید که به آن نیاز دارید.
- اگر کتابخانه ای در هیچ بخش کد استفاده نشود، خود Dart Editor به صورت warning اخطار میدهد و میتوانید آن را حذف کنید.
دستور زیر را در تابع main بنویسید تا رویداد مربوط به ورود اطلاعات در فیلد input را مدیریت نمایید.
void main() { querySelector('#inputName').onInput.listen(updateBadge); }
توضیحات
- تابع querySelector() در کتابخانه dart:html تعریف شده است و یک المنت DOM را جستجو مینماید. پارامتر ورودی آن یک selector میباشد که در اینجا فیلد input را توسط #inputName جستجو نمودیم که یک ID Selector میباشد.
- نوع خروجی این متد یک شی از نوع DOM میباشد.
- تابع onInput.Listen() رویدادی را برای پاسخگویی به ورود اطلاعات در فیلد input تعریف میکند. زمانی که کاربر اطلاعاتی را وارد نماید، تابع updateBadge فراخوانی میگردد.
- رویداد input زمانی رخ میدهد که کاربر کلیدی را از صفحه کلید فشار دهد.
- رشتهها همانند جاوا اسکریپت میتوانند در " یا ' قرار بگیرند.
تابع زیر را به صورت top-level یعنی خارج از تابع main تعریف کنید.
... void updateBadge(Event e) { querySelector('#badgeName').text = e.target.value; }
توضیحات
- این تابع محتوای المنت badgeName را به محتوای وارد شده در فیلد input تغییر میدهد.
- پارامتر ورودی این تابع شی e از نوع Event میباشد و به همین دلیل میتوانیم این تابع را یک Event Handler بنامیم.
- e.target به شی ای اشاره میکند که موجب رخداد رویداد شده است و در اینجا همان فیلد input میباشد
- با نوشتن کد فوق یک warning را مشاهده میکنید که بیان میکند ممکن است خصوصیت value برای e.target وجود نداشته باشد. برای حل این مسئله کد را بصورت زیر تغییر دهید.
... void updateBadge(Event e) { querySelector('#badgeName').text = (e.target as InputElement).value; }
توضیحات
- کلمه کلیدی as به منظور تبدیل نوع استفاده میشود که e.target را به یک InputElement تبدیل میکند.
همانند گام اول برنامه را اجرا کنید و نتیجه را مشاهده نمایید. با تایپ کردن در فیلد input به صورت همزمان در کادر قرمز رنگ نیز نتیجه تایپ را مشاهده مینمایید.
- در استاندارد 9147 ، جای ژ و پ مطابق صفحه کلیدی که در بازار ایران فروخته میشود نیست (و باید به همه پاسخگو باشید که چرا اینطوری است).
- ی و ک در آن اصلاح شده و دیگر عربی نیست که چقدر هم خوب، اما من تعداد زیادی برنامه و همچنین تعداد قابل توجهی فایل word دارم که مطابق استاندارد 2091 تهیه شدهاند. مشکلات ی و ک فارسی و عربی را هم در اینجا ذکر کردهام و به دنبال باگهای جدید در برنامهها نمیگردم.
- با ی و ک فارسی اگر در گوگل جستجو کنید تعداد پاسخهای مرتبط یافت شده بسیار بسیار کمتر از حالتی خواهد بود که با ی و ک عربی جستجو کنید.
- در یک سازمان باید رویهای واحد در این مورد برقرار باشد. یا همه باید از 2091 استفاده کنند و یا همه از 9147.
نسخهی کامل استاندارد 9147 را از سایت آقای اخگری میتوانید دریافت نمائید:
به همین دلایل خصوصا قسمت دوم (هر چند ممکن است با آن موافق نباشید)، نیاز به صفحه کلید مطابق استاندارد 2091 نسخهی 64 بیتی داشتم.
برای تهیه صفحه کلید فارسی از برنامه Microsoft Keyboard Layout Creator استفاده میشود که نسخهی 1.4 آنرا از آدرس زیر میتوانید دریافت نمائید. این نسخه قابلیت تولید فایلهای dll مرتبط 64 بیتی را هم دارد:
یکی از قابلیتهای این برنامه بارگذاری صفحه کلید فارسی جاری سیستم است:
صفحه کلید استاندارد 2091 پروژه وب فارسی را اگر با آن باز کرده و سپس از منوی Project گزینهی Build DLL and setup package را انتخاب نمائید، با خطای زیر متوقف خواهید شد:
ERROR: 'VK_SPACE' in Shift State 'Shift' must be made up of white space character(s), but is defined as '' (U+200c) instead.
39 SPACE 0 0020 200c 0020 -1 // SPACE, ZERO WIDTH NON-JOINER, SPACE, <none>
0 //Column 4 1 //Column 5 : Shft 2 //Column 6 : Ctrl 3 //Column 7 : Shft Ctrl
اما این برنامهی محترم دقیقا همین مورد را غلط گرفته و فایل dll نهایی را تولید نمیکند (مطابق خطایی که ذکر شد).
برنامهی Microsoft Keyboard Layout Creator هم با دات نت فریم ورک نوشته شده است. اگر کمی با reflector به آنالیز آن بپردازیم به کلاس Accept و متد زیر خواهیم رسید:
private bool VerifySpaceBarIntegrity(ShiftState ss, bool fMustBeDefined)
if (!Utilities.IsSpacing(ss.Characters)) { this.WriteErrorToLogFile("SpaceKeyNeedsWhiteSpaceCharacters", new string[] { "VK_" + Utilities.VkStringOfIvk(ss.VK), this.m_stStateLabel[ss.State], ss.Characters, Utilities.FromCharacterToUPlusForm(ss.Characters) }); flag = false; /*اینجا باید اصلاح شود!*/ }
با استفاده از این نسخه (ابتدا برنامه اصلی را نصب کرده و سپس فایل exe را جایگزین نمائید)، به راحتی میتوان نسخهی استاندارد و اصلی 2091 را بارگذاری و مجددا کامپایل کرد (بدون توقف کامپایل به خاطر فقط یک نیم فاصلهی غیرمعتبر از دیدگاه نویسندگان اصلی برنامه). به این صورت فایلهای 64 بیتی لازم هم تولید میشوند ( ممکن است در حین کار با برنامهی patch شده، نمایش خطای کار با نیم فاصله را مجددا دریافت کنید؛ اما مهم نیست . نمونهی وصله شده، بدون مشکل حاصل را کامپایل میکند که در نمونهی اصلی اینطور نیست. یعنی پس از overwrite کردن فایل exe موجود، با نمونه patch شده، برنامه کار کامپایل را کامل میکند) که حاصل نهایی را از آدرس زیر میتوانید دریافت نمائید:
این فایلها دقیقا بر مبنای همان فایلهای پروژه وب فارسی استاندارد 2091 هستند؛ با این تفاوت که نسخههای غیر 32 بیتی هم در آن لحاظ شدهاند.
نصب این dllها هم از ویندوز ویستا به بعد داستان خودش را پیدا کرده؛ ابتدا باید take ownership شود، سپس میتوان فایل اصلی را به سادگی جایگزین کرد و سپس reboot .
برای این منظور ابتدا به پوشهی system32 ویندوز مراجعه کرده و فایل KBDFA.DLL را پیدا کنید.
در ادامه به پنجره خواص آن (کلیک راست و انتخاب properties) مراجعه و برگهی Security آنرا انتخاب کنید.
در این قسمت بر روی دکمهی Advanced کلیک نمائید.
در صفحهی باز شده به برگهی Owner مراجعه کنید.
در این قسمت بر روی دکمهی Edit کلیک نموده و کاربر خودتان را اضافه نمائید.
پس از طی این مرحله (یا همان take ownership) به برگه security مراجعه کرده و به کاربر خودتان دسترسی full control بدهید.
اکنون مجوز تغییر این فایل را بدون هیچ مشکلی خواهید یافت.
در پایان reboot را فراموش نکنید.
راه دوم:
یا برنامه نصاب حاصل از برنامهی Microsoft Keyboard Layout Creator، مدخل جدیدی را به صفحه کلیدهای قابل انتخاب ویندوز در کنترل پنل اضافه میکند که نیازی به reboot ندارد.
بر روی ماشینهای ویندوزی و ویندوزهای سرور، استفادهی از IIS به عنوان پروکسی درخواستها و ارسال آنها به Kestrel، روش توصیه شدهاست؛ از این جهت که حداقل قابلیتهایی مانند «port 80/443 forwarding»، مدیریت طول عمر برنامه، مدیریت مجوزهای SLL آن و خیلی از موارد دیگر توسط Kestrel پشتیبانی نمیشود.
معماری پردازش نگارشهای پیشین ASP.NET در IIS
در نگارشهای پیشین ASP.NET، همه چیز داخل پروسهای به نام w3wp.exe و یا IIS Worker Process پردازش میشود که در اصل چیزی نیست بجز همان IIS Application Pool. این AppPoolها، برنامههای ASP.NET شما را هاست میکنند و همچنین سبب وهله سازی و اجرای آنها نیز خواهند شد.
در اینجا درایور http.sys ویندوز، درخواستهای رسیده را دریافت کرده و سپس آنها را به سمت سایتهایی نگاشت شدهی به AppPoolهای مشخص، هدایت میکند.
معماری پردازش برنامههای ASP.NET Core در IIS
روش اجرای برنامههای ASP.NET Core با نگارشهای پیشین آنها کاملا متفاوت هستند؛ از این جهت که داخل پروسهی w3wp.exe اجرا نمیشوند. این برنامهها در یک پروسهی مجزای کنسول خارج از پروسهی w3wp.exe اجرا میشوند و حاوی وب سرور توکاری به نام کسترل (Kestrel) هستند.
این وب سرور، وب سروری است تماما دات نتی و به شدت برای پردازش تعداد بالای درخواستها بهینه سازی شدهاست؛ تا جایی که کارآیی آن در این یک مورد چند 10 برابر IIS است. هرچند این وب سرور فوق العاده سریع است، اما «تنها» یک وب سرور خام است و به همراه سرویسهای مدیریت وب، مانند IIS نیست.
در تصویر فوق مفهوم «پروکسی» بودن IIS را در حین پردازش برنامههای ASP.NET Core بهتر میتوان درک کرد. ابتدا درخواستهای رسیده به IIS میرسند و سپس IIS آنها را به طرف Kestrel هدایت میکند.
برنامههای ASP.NET Core، برنامههای کنسول متکی به خودی هستند که توسط دستور خط فرمان dotnet اجرا میشوند. این اجرا توسط ماژولی ویژه به نام AspNetCoreModule در IIS انجام میشود.
همانطور که در تصویر نیز مشخص است، AspNetCoreModule یک ماژول بومی IIS است و هنوز برای اجرا نیاز به IIS Application Pool دارد؛ با این تفاوت که در تنظیم AppPoolهای برنامههای ASP.NET Core، باید NET CLR Version. را به No managed code تنظیم کرد.
اینکار از این جهت صورت میگیرد که IIS در اینجا تنها نقش یک پروکسی هدایت درخواستها را به پروسهی برنامهی حاوی وب سرور Kestrel، دارد و کار آن وهله سازی NET Runtime. نیست. کار AspNetCoreModule این است که با اولین درخواست رسیدهی به برنامهی شما، آنرا بارگذاری کند. سپس درخواستهای رسیده را دریافت و به سمت برنامهی ASP.NET Core شما هدایت میکند (به این عملیات reverse proxy هم میگویند).
اگر دقت کرده باشید، برنامههای ASP.NET Core، هنوز دارای فایل web.config ایی با محتوای ذیل هستند:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <handlers> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/> </handlers> <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/> </system.webServer> </configuration>
یک نکته: در زمان publish برنامه، تنظیم و تبدیل مقادیر LAUNCHER_PATH و LAUNCHER_ARGS به معادلهای اصلی آنها صورت میگیرد (در ادامه مطلب بحث خواهد شد).
آیا واقعا هنوز نیازی به استفادهی از IIS وجود دارد؟
هرچند میتوان Kestrel را توسط یک IP و پورت مشخص، عمومی کرد و استفاده نمود، اما حداقل در ویندوز چنین توصیهای نمیشود و بهتر است از IIS به عنوان یک front end proxy استفاده کرد؛ به این دلایل:
- اگر میخواهید چندین برنامه را بر روی یک وب سرور که از طریق پورتهای 80 و 443 ارائه میشوند داشته باشید، نمیتوانید از Kestrel به صورت مستقیم استفاده کنید؛ زیرا از مفهوم host header routing که قابلیت ارائهی چندین برنامه را از طریق پورت 80 و توسط یک IP میسر میکند، پشتیبانی نمیکند. برای اینکار نیاز به IIS و یا در حقیقت درایور http.sys ویندوز است.
- IIS خدمات قابل توجهی را به برنامهی شما ارائه میکند. برای مثال با اولین درخواست رسیده، به صورت خودکار آنرا اجرا و بارگذاری میکند؛ به همراه تمام مدیریتهای پروسهای که در اختیار برنامههای ASP.NET در طی سالیان سال قرار داشتهاست. برای مثال اگر پروسهی برنامهی شما در اثر استثنایی کرش کرد، دوباره با درخواست بعدی رسیده، حتما برنامه را بارگذاری و آمادهی خدمات دهی مجدد میکند.
- در اینجا میتوان تنظیمات SSL را بر روی IIS انجام داد و سپس درخواستهای معمولی را به Kestrel ارسال کرد. به این ترتیب با یک مجوز میتوان چندین برنامهی Kestrel را مدیریت کرد.
- IISهای جدید به همراه ماژولهای بومی بسیار بهینه و کم مصرفی برای مواردی مانند gzip compression of static content, static file caching, Url Rewriting هستند که با فعال سازی آنها میتوان از این قابلیتها، در برنامههای ASP.NET Core نیز استفاده کرد.
نحوهی توزیع برنامههای ASP.NET Core به IIS
روش اول: استفاده از دستور خط فرمان dotnet publish
برای این منظور به ریشهی پروژهی خود وارد شده و دستور dotnet publish را با توجه به پارامترهای ذیل اجرا کنید:
dotnet publish --framework netcoreapp1.0 --output "c:\temp\mysite" --configuration Release
{ "publishOptions": { "include": [ "wwwroot", "Features", "appsettings.json", "web.config" ] }, "scripts": { "precompile": [ "dotnet bundle" ], "prepublish": [ //"bower install" ], "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] } }
پس از انتقال این فایلها به سرور، مابقی مراحل آن مانند قبل است. یک Application جدید تعریف شده و سپس ابتدا مسیر آن مشخص میشود و نکتهی اصلی، انتخاب AppPool ایی است که پیشتر شرح داده شد:
برنامههای ASP.NET Core باید به AppPool ایی تنظیم شوند که NET CLR Version. آنها No Managed Code است. همچنین بهتر است به ازای هر برنامهی جدید یک AppPool مجزا را ایجاد کنید تا کرش یک برنامه تاثیر منفی را بر روی برنامهی دیگری نگذارد.
روش دوم: استفاده از ابزار Publish خود ویژوال استودیو
اگر علاقمند هستید که روش خط فرمان فوق را توسط ابزار publish ویژوال استودیو انجام دهید، بر روی پروژه در solution explorer کلیک راست کرده و گزینهی publish را انتخاب کنید. در صفحهای که باز میشود، بر روی گزینهی custom کلیک کرده و نامی را وارد کنید. از این نام پروفایل، جهت ساده سازی مراحل publish، در دفعات آتی فراخوانی آن استفاده میشود.
در صفحهی بعدی اگر گزینهی file system را انتخاب کنید، دقیقا همان مراحل روش اول تکرار میشوند:
سپس میتوانید فریم ورک برنامه و نوع ارائه را مشخص کنید:
و در آخر کار، Publish به این پوشهی مشخص شده که به صورت پیش فرض در ذیل پوشهی bin برنامهاست، صورت میگیرد.
روش عیب یابی راه اندازی اولیهی برنامههای ASP.NET Core
در اولین سعی در اجرای برنامهی ASP.NET Core بر روی IIS به این خطا رسیدم:
در event viewer ویندوز چیزی ثبت نشده بود. اولین کاری را که در این موارد میتوان انجام داد به این صورت است. از طریق خط فرمان به پوشهی publish برنامه وارد شوید (همان پوشهای که توسط IIS عمومی شدهاست). سپس دستور dotnet prog.dll را صادر کنید. در اینجا prog.dll نام dll اصلی برنامه یا همان نام پروژه است:
همانطور که مشاهده میکنید، برنامه به دنبال پوشهی bower_components ایی میگردد که کار publish آن انجام نشدهاست (این پوشه در تنظیمات آغازین برنامه عمومی شدهاست و در لیست include قسمت publishOptions فایل project.json فراموش شدهاست).
روش دوم، فعال سازی stdoutLogEnabled موجود در فایل وب کانفیگ، به true است. در اینجا web.config نهایی تولیدی توسط عملیات publish را مشاهده میکنید که در آن پارامترهای processPath و arguments مقدار دهی شدهاند (همان قسمت postpublish فایل project.json). در اینجا مقدار stdoutLogEnabled به صورت پیش فرض false است. اگر true شود، همان خروجی تصویر فوق را در پوشهی logs خواهید یافت:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <handlers> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" /> </handlers> <aspNetCore processPath="dotnet" arguments=".\Core1RtmEmptyTest.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" /> </system.webServer> </configuration>
Warning: Could not create stdoutLogFile \\?\D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest\bin\Release\PublishOutput\logs\stdout_10064_201672893654.log, ErrorCode = -2147024893.
حداقلهای یک هاست ویندوزی که میخواهد برنامههای ASP.NET Core را ارائه دهد
پس از نصب IIS، نیاز است ASP.NET Core Module نیز نصب گردد. برای اینکار اگر بستهی NET Core Windows Server Hosting. را نصب کنید، کافی است:
https://go.microsoft.com/fwlink/?LinkId=817246
این بسته به همراه NET Core Runtime, .NET Core Library. و ASP.NET Core Module است. همچنین همانطور که عنوان شد، برنامههای ASP.NET Core باید به AppPool ایی تنظیم شوند که NET CLR Version. آنها No Managed Code است. اینها حداقلهای راه اندازی یک برنامهی ASP.NET Core بر روی سرورهای ویندوزی هستند.
هنوز فایل app_offline.htm نیز در اینجا معتبر است
یکی از خواص ASP.NET Core Module، پردازش فایل خاصی است به نام app_offline.htm. اگر این فایل را در ریشهی سایت قرار دهید، برنامه پردازش تمام درخواستهای رسیده را قطع خواهد کرد و سپس پروسهی برنامه خاتمه مییابد. هر زمانیکه این فایل حذف شد، مجددا با درخواست بعدی رسیده، برنامه آمادهی پاسخگویی میشود.
روش نگاشت محتوای یک سایت استاتیک در یک Container که وب سرور است
فرض کنید یک سایت استاتیک بوت استرپی را تهیه کردهاید و قصد دارید آنرا توسط وب سرور nginx، هاست کنید. برای اینکار، چندین گزینه پیش روی ما هستند:
گزینهی اول: دریافت image مربوط به nginx، سپس ایجاد یک container از آن و در آخر با استفاده از «روش به اشتراک گذاری فایل سیستم میزبان با کانتینرها» که در قسمت قبل بررسی کردیم، این وب سایت را آمادهی اجرا و دسترسی میکنیم.
گزینهی دوم: کپی کردن فایلهای وب سایت از سیستم میزبان، به درون فایل سیستم خود container.
گزینهی سوم: ایجاد یک image سفارشی که از ابتدا به همراه فایلهای وب سایت استاتیک ما است و در این حالت تنها کافی است این image را تبدیل به container اجرایی کنیم.
روش اول: به اشتراک گذاری فایل سیستم میزبان با کانتینر وب سرور جهت هاست آن
در قسمت قبل، یک فایل tar ایجاد شدهی در سیستم میزبان ویندوزی را با یک کانتینر لینوکسی به اشتراک گذاشتیم تا بتوانیم محتویات آنرا استخراج کنیم. در اینجا قصد داریم پوشهی وب سایت استاتیک خود را که در سیستم میزبان ویندوزی قرار دارد، با وب سرور nginx که توسط یک container در حال اجرا است، به اشتراک بگذاریم تا آنرا هاست کند.
فرض کنید وب سایت استاتیک ما در مسیر c:\users\vahid\mysite سیستم میزبان قرار دارد که داخل آن یک فایل index.html و تعدادی فایل css و js آمادهی برای هاست شدن، وجود دارند. برای هاست آن توسط nginx، از دستور زیر استفاده خواهیم کرد:
docker run --rm -it -p 8080:80 -v c:\users\vahid\mysite:/usr/share/nginx/html nginx
- سوئیچ rm سبب میشود تا پس از خاتمهی کار nginx، این container نیز حذف شود.
- از سوئیچ it استفاده شدهاست تا با فشردن ctrl+c، بتوانیم پروسهی container را خاتمه دهیم و پس از آن، برنامهی nginx دیگر در background در حال اجرا نباشد (اجرای آن در foreground).
- سپس پورت 8080 سیستم میزبان، به پورت 80 وب سرور nginx نگاشت شدهاست. چون containerها دارای network stack خاص خودشان هستند (که آنرا در قسمت سوم بررسی کردیم)، پورت 80 آنها با پورت 80 سیستم میزبان تداخل نمیکند و اگر برای مثال بر روی پورت 80 سیستم جاری، IIS در حال اجرا باشد، سبب عدم اجرا شدن وب سرور nginx به دلیل تداخل پورتها نمیشود.
- در ادامه روش volume mount را مشاهده میکنید که در قسمت قبل بررسی کردیم. مسیر c:\users\vahid\mysite سیستم میزبان، به مسیر ویژهی /usr/share/nginx/html داخل container نگاشت شدهاست. این مسیر، یک مسیر استاندارد بوده و در مستندات docker hub این وب سرور، ذکر شدهاست.
- در آخر هم نام image این وب سرور را ذکر کردهایم.
پس از اجرای این دستور، اگر nginx پیشتر دریافت نشده باشد، image آن دریافت شده، یک container بر اساس آن ساخته میشود و سپس با پارامترهایی که توضیح دادیم، اجرا خواهد شد. اکنون اگر در سیستم میزبان، مسیر http://localhost:8080 را در مرورگر باز کنید، وب سایت استاتیک خود را مشاهده خواهید کرد.
روش دوم: کپی کردن فایلهای وب سایت از سیستم میزبان، به درون فایل سیستم خود container
همانطور که در قسمت سوم نیز بررسی کردیم، فایل سیستم مربوط به هاست، به طور کامل از فایل سیستم container، جدا و ایزوله است و بدون volume mount، یک container نمیتواند به فایلهای میزبان خود دسترسی پیدا کند. بنابراین گزینهی دیگری که در اینجا وجود خواهد داشت، کپی کردن فایلهای میزبان و انتقال آنها به container میباشد؛ شبیه به کپی کردن فایلها از یک کامپیوتر موجود در شبکه به کامپیوتر دیگری در آن.
برای این منظور ابتدا nginx را در پسزمینه اجرا میکنیم:
docker run -d -p 8080:80 --name nginx nginx
پس از اجرای این دستور و بازگشت به command prompt، جهت اطمینان حاصل کردن از اجرای آن در پس زمینه، دستور docker ps را صادر میکنیم که لیست آن، حاوی گزارشی از containerهای در حال اجرا است.
اکنون توسط دستور ویژهی docker exec، میخواهیم درون یک container در حال اجرا، پروسهای را اجرا کنیم. یعنی با اینکه پروسهی nginx داخل این container در حال اجرا است، برای مثال میخواهیم یک shell را نیز داخل آن اجرا کنیم:
docker exec -it nginx bash
cd /usr/share/nginx/html
ls mv index.html index2.html exit
docker cp c:\users\vahid\mysite nginx:/usr/share/nginx/html
docker exec nginx ls /usr/share/nginx/html
روش سوم: ایجاد یک image سفارشی که از ابتدا به همراه فایلهای وب سایت استاتیک ما است
در روش دوم، موفق شدیم که فایلهای مدنظر خود را به درون container در حال اجرا کپی کنیم. اکنون میخواهیم یک snapshot را از آن تهیه کنیم؛ شبیه به کاری که با ماشینهای مجازی نیز انجام میشود و این روشی است که از آن برای ساخت یک image سفارشی استفاده میشود. برای این منظور از دستور docker commit استفاده میشود تا تصویری را از وضعیت یک container در حال اجرا، در آن لحظه تهیه کنیم:
docker commit nginx mysite:nginx
اکنون پس از اجرای این دستور، با استفاده از فرمان docker images میتوان مشاهده کرد که image جدید mysite، با tag ای معادل nginx، ایجاد شدهاست.
در ادامه برای اجرای این image جدید، میتوان از دستور زیر استفاده کرد:
docker run -d -p 8090:80 --name mysite mysite:nginx
اکنون اگر در سیستم میزبان، مسیر http://localhost:8090 را در مرورگر باز کنید، وب سایت استاتیک خود را مشاهده خواهید کرد و یا توسط دستور زیر میتوانید فایلهای موجود در پوشهی html وب سرور nginx این container جدید در حال اجرا را ملاحظه نمائید:
docker exec mysite ls /usr/share/nginx/html
نگاهی به لایههای یک Image در مقایسه با یک Container
زمانیکه خواستیم image جدید و سفارشی خاص خود را ایجاد کنیم، با image اصلی nginx شروع کردیم. اولین لایهی موجود در این image، سیستم عاملی است که میتواند آنرا اجرا کند. برفراز این لایه، لایهی خود nginx قرار گرفتهاست. اگر خواستید تاریخچهی ایجاد یک image را مشاهده کنید، از دستور docker history nginx استفاده نمائید. خروجی آن لیست دستوراتی را نمایش میدهد که برای ساخت این image مورد استفاده قرار گرفتهاند. البته دستور docker history nginx --no-trunc، اطلاعات بیشتری را با نمایش لیست کامل و خلاصه نشدهی دستورات، ارائه میدهد. این دستورات را در صفحهی docker hub هر image نیز میتوان مشاهده کرد. در قسمت full description هر image، در ابتدای توضیحات، قسمتی است به نام supported tags and respective dockerfile links. در اینجا هر tag نامبرده شده، در حقیقت لینکی است به یک فایل که دقیقا همین دستورات را لیست کردهاست. به این فایل، docker file گفته میشود که روش ساخت یک image را توضیح میدهد. هدف آن، خودکار سازی اجرای دستوراتی است که سبب ساخت یک image میشوند.
در ادامه اگر از این image، یک container را ایجاد کنیم، این container هر دو لایهی OS و Framework را به همراه خواهد داشت؛ به علاوهی لایهی دیگری به نام Container/Run که میتوان فایلهای آنرا خواند و یا در آن نوشت. بنابراین لایهای که فایلهای وب سایت استاتیک ما در آن کپی شدند، دقیقا همین لایهاست.
و زمانیکه از یک container تصویری تهیه میشود، تغییراتی را که به فایل سیستم آن ایجاد کردهایم، به صورت یک لایهی جدید بر روی لایههای قبلی آن image، ظاهر و ثبت میشود. برای اثبات این موضوع، میتوان از دستور docker diff nginx استفاده کرد. در اینجا nginx نام container ای است که میخواهیم تغییرات آنرا با image قبلی که بر پایهی آن ایجاد شدهاست، مشاهده کنیم.
تبدیل دستورات docker به یک docker file
تا اینجا یک چنین دستوراتی را برای اجرای کانتینر nginx، کپی فایلها به آن و سپس تهیهی یک تصویر از آن، اجرا کردیم:
docker run -d -p 8080:80 --name nginx nginx docker cp c:\users\vahid\mysite nginx:/usr/share/nginx/html docker commit nginx mysite:nginx
بجای سطر اول، تنها نام image ای را که میخواهیم کار را بر مبنای آن انجام دهیم، ذکر میکنیم:
FROM nginx
COPY mysite /usr/share/nginx/html
سپس برای اجرای این فایل، بجای دستور docker commit آخر، از دستور زیر استفاده میکنیم:
docker build -f Dockerfile -t mysite:nginx-df .
docker build -t mysite:nginx-df .
تگ این image را نیز متفاوت با قبلیها انتخاب کردهایم؛ nginx-df بجای مقدار قبلی.
در این حالت اگر دستور آخر را اجرا کنیم، دستور docker images گزارش اضافه شدن این image جدید را ارائه خواهد داد.
مرجع کامل ساخت Dockerfileها را در اینجا میتوانید مطالعه کنید.
ساخت یک image سفارشی برای هاست یک وب سایت استاتیک در IIS
تا اینجا از وب سرور لینوکسی nginx برای هاست وب سایت استاتیک خود استفاده کردیم. در ادامه میخواهیم از وب سرور IIS برای اینکار استفاده نمائیم. بنابراین ابتدا نیاز است یا از ویندوز سرور استفاده کنیم و یا میتوان با کلیک راست بر روی آیکن Docker در قسمت Tray Icons ویندوز، به Windows Containers سوئیچ کرد و سپس به صورت زیر عمل نمود.
اینبار محتوای Dockerfile ای که کنار پوشهی mysite قرار میگیرد، به صورت زیر خواهد بود:
FROM microsoft/iis:nanoserver COPY mysite c:/inetpub/wwwroot
در ادامه با استفاده از دستور زیر و اجرای فایل Dockerfile، این image جدید را با tag ای به نام iis ایجاد میکنیم:
docker build -t mysite:iis .
به اشتراک گذاری imageهای سفارشی در Docker Hub
برای به اشتراک گذاری imageهای سفارشی خود در Docker Hub، نیاز است tag آنها را توسط دستور docker tag مطابق فرمت ویژهی docker hub ویرایش کرد:
docker tag mysite:nginx-df my_user_name/some_name:new_tag_name
و در آخر برای انتشار آن میتوان از دستور docker push استفاده کرد:
docker push my_user_name/some_name:new_tag_name
پس از پایان کار اگر به سایت docker hub و مخازن خود مراجعه کنید، این image جدید قابل مشاهده خواهد بود.
اگر استاندارد OpenID Connect را بررسی کنیم، از مجموعهای از دستورات و رهنمودها تشکیل شدهاست. بنابراین نیاز به کامپوننتی داریم که این استاندارد را پیاده سازی کرده باشد تا بتوان بر اساس آن یک Identity Provider را تشکیل داد و پیاده سازی مباحثی که در قسمت قبل بررسی شدند مانند توکنها، Flow، انواع کلاینتها، انواع Endpoints و غیره چیزی نیستند که به سادگی قابل انجام باشند. اینجا است که IdentityServer 4، به عنوان یک فریم ورک پیاده سازی کنندهی استانداردهای OAuth 2 و OpenID Connect مخصوص ASP.NET Core ارائه شدهاست. این فریم ورک توسط OpenID Foundation تائید شده و داری مجوز رسمی از آن است. همچنین جزئی از NET Foundation. نیز میباشد. به علاوه باید دقت داشت که این فریم ورک کاملا سورس باز است.
نصب و راه اندازی IdentityServer 4
همان مثال «قسمت دوم - ایجاد ساختار اولیهی مثال این سری» را در نظر بگیرید. داخل آن پوشههای جدید src\IDP\DNT.IDP را ایجاد میکنیم.
نام دلخواه DNT.IDP، به پوشهی جدیدی اشاره میکند که قصد داریم IDP خود را در آن برپا کنیم. نام آن را نیز در ادامهی نامهای پروژههای قبلی که با ImageGallery شروع شدهاند نیز انتخاب نکردهایم؛ از این جهت که یک IDP را قرار است برای بیش از یک برنامهی کلاینت مورد استفاده قرار دهیم. برای مثال میتوانید از نام شرکت خود برای نامگذاری این IDP استفاده کنید.
اکنون از طریق خط فرمان به پوشهی src\IDP\DNT.IDP وارد شده و دستور زیر را صادر کنید:
dotnet new web
اولین کاری را که در اینجا انجام خواهیم داد، مراجعه به فایل Properties\launchSettings.json آن و تغییر شماره پورتهای پیشفرض آن است تا با سایر پروژههای وبی که تاکنون ایجاد کردهایم، تداخل نکند. برای مثال در اینجا شماره پورت SSL آنرا به 6001 تغییر دادهایم.
اکنون نوبت به افزودن میانافزار IdentityServer 4 به پروژهی خالی وب است. اینکار را نیز توسط اجرای دستور زیر در پوشهی src\IDP\DNT.IDP انجام میدهیم:
dotnet add package IdentityServer4
در ادامه نیاز است این میانافزار جدید را معرفی و تنظیم کرد. به همین جهت فایل Startup.cs پروژهی خالی وب را گشوده و به صورت زیر تکمیل میکنیم:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddIdentityServer() .AddDeveloperSigningCredential(); }
- متد الحاقی AddDeveloperSigningCredential کار تنظیمات کلید امضای دیجیتال توکنها را انجام میدهد. در نگارشهای قبلی IdentityServer، اینکار با معرفی یک مجوز امضاء کردن توکنها انجام میشد. اما در این نگارش دیگر نیازی به آن نیست. در طول توسعهی برنامه میتوان از نگارش Developer این مجوز استفاده کرد. البته در حین توزیع برنامه به محیط ارائهی نهایی، باید به یک مجوز واقعی تغییر پیدا کند.
تعریف کاربران، منابع و کلاینتها
مرحلهی بعدی تنظیمات میانافزار IdentityServer4، تعریف کاربران، منابع و کلاینتهای این IDP است. به همین جهت یک کلاس جدید را به نام Config، در ریشهی پروژه ایجاد و به صورت زیر تکمیل میکنیم:
using System.Collections.Generic; using System.Security.Claims; using IdentityServer4.Models; using IdentityServer4.Test; namespace DNT.IDP { public static class Config { // test users public static List<TestUser> GetUsers() { return new List<TestUser> { new TestUser { SubjectId = "d860efca-22d9-47fd-8249-791ba61b07c7", Username = "User 1", Password = "password", Claims = new List<Claim> { new Claim("given_name", "Vahid"), new Claim("family_name", "N"), } }, new TestUser { SubjectId = "b7539694-97e7-4dfe-84da-b4256e1ff5c7", Username = "User 2", Password = "password", Claims = new List<Claim> { new Claim("given_name", "User 2"), new Claim("family_name", "Test"), } } }; } // identity-related resources (scopes) public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile() }; } public static IEnumerable<Client> GetClients() { return new List<Client>(); } } }
- این کلاس استاتیک، اطلاعاتی درون حافظهای را برای تکمیل دموی جاری ارائه میدهد.
- ابتدا در متد GetUsers، تعدادی کاربر آزمایشی اضافه شدهاند. کلاس TestUser در فضای نام IdentityServer4.Test قرار دارد. در کلاس TestUser، خاصیت SubjectId، بیانگر Id منحصربفرد هر کاربر در کل این IDP است. سپس نام کاربری، کلمهی عبور و تعدادی Claim برای هر کاربر تعریف شدهاند که بیانگر اطلاعاتی اضافی در مورد هر کدام از آنها هستند. برای مثال نام و نام خانوادگی جزو خواص کلاس TestUser نیستند؛ اما منعی هم برای تعریف آنها وجود ندارد. اینگونه اطلاعات اضافی را میتوان توسط Claims به سیستم اضافه کرد.
- بازگشت Claims توسط یک IDP مرتبط است به مفهوم Scopes. برای این منظور متد دیگری به نام GetIdentityResources تعریف شدهاست تا لیستی از IdentityResourceها را بازگشت دهد که در فضای نام IdentityServer4.Models قرار دارد. هر IdentityResource، به یک Scope که سبب دسترسی به اطلاعات Identity کاربران میشود، نگاشت خواهد شد. در اینجا چون از پروتکل OpenID Connect استفاده میکنیم، ذکر IdentityResources.OpenId اجباری است. به این ترتیب مطمئن خواهیم شد که SubjectId به سمت برنامهی کلاینت بازگشت داده میشود. برای بازگشت Claims نیز باید IdentityResources.Profile را به عنوان یک Scope دیگری مشخص کرد که در متد GetIdentityResources مشخص شدهاست.
- در آخر نیاز است کلاینتهای این IDP را نیز مشخص کنیم (در مورد مفهوم Clients در قسمت قبل بیشتر توضیح داده شد) که اینکار در متد GetClients انجام میشود. فعلا یک لیست خالی را بازگشت میدهیم و آنرا در قسمتهای بعدی تکمیل خواهیم کرد.
افزودن کاربران، منابع و کلاینتها به سیستم
پس از تعریف و تکمیل کلاس Config، برای معرفی آن به IDP، به کلاس آغازین برنامه مراجعه کرده و آنرا به صورت زیر تکمیل میکنیم:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddIdentityServer() .AddDeveloperSigningCredential() .AddTestUsers(Config.GetUsers()) .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryClients(Config.GetClients()); }
افزودن میان افزار IdentityServer به برنامه
پس از انجام تنظیمات مقدماتی سرویسهای برنامه، اکنون نوبت به افزودن میانافزار IdentityServer است که در کلاس آغازین برنامه به صورت زیر تعریف میشود:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); }
آزمایش IDP
اکنون برای آزمایش IDP، به پوشهی src\IDP\DNT.IDP وارد شده و دستور dotnet run را اجرا کنید:
همانطور که ملاحظه میکنید، برنامهی IDP بر روی پورت 6001 قابل دسترسی است. برای آزمایش Web API آن، آدرس discovery endpoint این IDP را به صورت زیر در مرورگر وارد کنید:
https://localhost:6001/.well-known/openid-configuration
در این تصویر، مفاهیمی را که در قسمت قبل بررسی کردیم مانند authorization_endpoint ،token_endpoint و غیره، مشاهده میکنید.
افزودن UI به IdentityServer
تا اینجا میانافزار IdentityServer را نصب و راه اندازی کردیم. در نگارشهای قبلی آن، UI به صورت پیشفرض جزئی از این سیستم بود. در این نگارش آنرا میتوان به صورت جداگانه دریافت و به برنامه اضافه کرد. برای این منظور به آدرس IdentityServer4.Quickstart.UI مراجعه کرده و همانطور که در readme آن ذکر شدهاست میتوان از یکی از دستورات زیر برای افزودن آن به پروژهی IDP استفاده کرد:
الف) در ویندوز از طریق کنسول پاورشل به پوشهی src\IDP\DNT.IDP وارد شده و سپس دستور زیر را وارد کنید:
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.ps1'))
\curl -L https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.sh | bash
یک نکته: در ویندوز اگر در نوار آدرس هر پوشه، عبارت cmd را وارد و enter کنید، کنسول خط فرمان ویندوز در همان پوشه باز خواهد شد. همچنین در اینجا از ورود عبارت powershell هم پشتیبانی میشود:
بنابراین در نوار آدرس پوشهی src\IDP\DNT.IDP، عبارت powershell را وارد کرده و سپس enter کنید. پس از آن دستور الف را وارد (copy/paste) و اجرا کنید.
به این ترتیب فایلهای IdentityServer4.Quickstart.UI به پروژهی IDP جاری اضافه میشوند.
- پس از آن اگر به پوشهی Views مراجعه کنید، برای نمونه ذیل پوشهی Account آن، Viewهای ورود و خروج به سیستم قابل مشاهده هستند.
- در پوشهی Quickstart آن، کدهای کامل کنترلرهای متناظر با این Viewها قرار دارند.
بنابراین اگر نیاز به سفارشی سازی این Viewها را داشته باشید، کدهای کامل کنترلرها و Viewهای آن هم اکنون در پروژهی IDP جاری در دسترس هستند.
نکتهی مهم: این UI اضافه شده، یک برنامهی ASP.NET Core MVC است. به همین جهت در انتهای متد Configure، ذکر میان افزارهای UseStaticFiles و همچنین UseMvcWithDefaultRoute انجام شدند.
اکنون اگر برنامهی IDP را مجددا با دستور dotnet run اجرا کنیم، تصویر زیر را میتوان در ریشهی سایت، مشاهده کرد که برای مثال لینک discovery endpoint در همان سطر اول آن ذکر شدهاست:
همچنین همانطور که در قسمت قبل نیز ذکر شد، یک IDP حتما باید از طریق پروتکل HTTPS در دسترس قرار گیرد که در پروژههای ASP.NET Core 2.1 این حالت، جزو تنظیمات پیشفرض است.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
- ViewUsers
- CreateUser
- EditUser
- DeleteUser
همانطور که مشاهده میکنید، المنتهایی در صفحه وجود دارند که کاربر X نباید آنها را مشاهده کند. از جمله دکمه حذف کاربر و دکمه ایجاد کاربر. برای مخفی کردن آنها چه راهحلی را میتوان ارائه داد؟ شاید بخواهید برای اینکار از ngIf* استفاده کنید. برای اینکار کافی است دست بکار شوید تا مشکلاتی را که در این روش به آنها بر میخورید، متوجه شوید. از جمله این مشکلات میتوان به پیچیدگی بسیار زیاد و وجود کدهای تکراری در هر کامپوننت اشاره کرد (در بهترین حالت هر کامپوننت باید سرویس حاوی دسترسیها را در خود تزریق کرده و با استفاده از توابعی، وجود یا عدم وجود دسترسی را بررسی کند).
راهحل بهتر، استفاده از یک Directive سفارشی است. همچنین ماژول Ng2Permission علاوه بر فراهم کردن Directive جهت مدیریت المنتهای روی صفحه، امکاناتی را جهت نگهداری و تعریف دسترسیهای جدید و همچنین محافظت از Routeها فراهم کرده است. این ماژول الهام گرفته از ماژول angular-permission میباشد.
کافی است با استفاده از دستور زیر این ماژول را نصب کنید:
npm install angular2-permission --save
بعد از نصب، ماژول Ng2Permission را در قسمت imports در ماژول اصلی برنامه، اضافه کنید.
import { Ng2Permission } from 'angular2-permission'; @NgModule({ imports: [ Ng2Permission ] })
مدیریت دسترسیها
توضیحات | امضاء |
تعریف دسترسیهای جدید | define(permissions: Array<string>): void |
افزودن دسترسی جدید | add(permission: string ) : void |
حذف دسترسی مشخص شده | remove(permission: string ) : void |
برسی اینکه دسترسی قبلا تعریف شده است؟ | hasDefined( permission : string ) : boolean |
برسی اینکه حداقل یکی از دسترسیهای ورودی قبلا تعریف شده است؟ | hasOneDefined(permissions: Array < string > ) : boolean |
حذف تمامی دسترسیها | clearStore( ) : void |
دریافت تمامی دسترسیهای تعریف شده | get store ( ) : Array < string> |
Emitter جهت تغییر در دسترسیها | get permissionStoreChangeEmitter ( ) : EventEmitter< an y> |
برای مثال جهت تعریف دسترسیهای جدید، کافی است سرویس PermissionService را تزریق کرده و با استفاده از متدهای define اقدام به اینکار کنید:
import { PermissionService } from 'angular2-permission'; @Component({ […] }) export class LoginComponent implements OnInit { constructor(private _permissionService: PermissionService) { this._permissionService.define(['ViewUsers', 'CreateUser', 'EditUser', 'DeleteUser']); } }
آنچه که واضح است، این است که لیست دسترسیها میتوانند از سمت سرور تامین شوند.
محافظت از مسیرهای تعریف شده
import { PermissionGuard, IPermissionGuardModel } from 'angular2-permission'; […] const routes: Routes = [ { path: 'login', component: LoginComponent, children: [] }, { path: 'users', component: UserListComponent, canActivate: [PermissionGuard], data: { Permission: { Only: ['ViewUsers'], RedirectTo: '403' } as IPermissionGuardModel }, children: [] }, { path: 'users/create', component: UserCreateComponent, canActivate: [PermissionGuard], data: { Permission: { Only: ['CreateUser'], RedirectTo: '403' } as IPermissionGuardModel }, children: [] }, { path: '403', component: AccessDeniedComponent, children: [] } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
توضیحات | خصوصیت |
فقط دسترسیهای تعریف شده در این قسمت، امکان پیمایش به این مسیر را خواهند داشت. | Only |
تمامی دسترسیهای تعریف شده، به جز دسترسیهای تعریف شده در این قسمت، امکان پیمایش به این مسیر را خواهند داشت. | Except |
در صورتیکه تقاضای غیر مجازی جهت پیمایش به این مسیر صادر شد، کاربر را به این مسیر هدایت میکند. | RedirectTo |
{ path: 'users/create', component: UserCreateComponent, canActivate: [PermissionGuard], data: { Permission: { Only: ['Admin', 'CreateUser'], RedirectTo: '403' } as IPermissionGuardModel }, children: [] }
محافظت از المنتهای صفحه
توضیحات | Input type | Directive |
فقط دسترسیهای تعریف شده در این دایرکتیو امکان مشاهده (به صورت پیش فرض) المنت را دارند. | Arrayy<string> | hasPermission |
تمامی دسترسیهای موجود، به جز دسترسیهای تعریف شده در این دایرکتیو امکان مشاهده (به صورت پیش فرض) المنت را دارند. | Array<string> | exceptPermission |
استراتژی برخورد با المنت، هنگامیکه کاربر دسترسی به المنت دارد. | string | Function | onAuthorizedPermission |
استراتژی برخورد با المنت، هنگامیکه کاربر دسترسی به المنت را ندارد. | string | Function | onUnauthorizedPermission |
<button type="button" [hasPermission]="['DeleteUser']"> <span aria-hidden="true"></span> Delete </button>
در صورتیکه بیش از یک دسترسی مد نظر باشد، با کاما از هم جدا خواهند شد.
<button type="button" [hasPermission]="[ 'Admin', 'DeleteUser']"> <span aria-hidden="true"></span> Delete </button>
<button type="button" [exceptPermission]="['GeustUser']"> <span aria-hidden="true"></span> Delete </button>
<button type="button" [hasPermission]="['GeustUser']" onAuthorizedPermission="enable" onUnauthorizedPermission="disable"> <span aria-hidden="true"></span> Delete </button>
رفتار | مقدار |
حذف خصوصیت disabled از المنت | enable |
افزودن خصوصیت disabled به المنت | disable |
تنظیم استایل display به inherit | show |
تنظیم استایل display به none | hide |
@Component({ selector: 'app-user-list', templateUrl: './user-list.component.html', styleUrls: ['./user-list.component.css'] }) export class UserListComponent { constructor() { } OnAuthorizedPermission(element: ElementRef) { element.nativeElement.style.visibility ="inherit"; } OnUnauthorizedPermission(element: ElementRef) { element.nativeElement.style.visibility = "hidden"; } }
<button [hasPermission]="['CreateUser']" [onAuthorizedPermission]="OnAuthorizedPermission" [onUnauthorizedPermission]="OnUnauthorizedPermission"> <span aria-hidden="true"></span> Add New User </button>
ساخت ربات تلگرامی با #C
[test](http://www.example.com/)