ASP.NET MVC #6
ولی به نظر من توی mvc قضیه برعکسه. وقتی قرار واسط کاربر طراحی بشه ، و از روش ترجیح داده شده strongly type view استفاده بشه ( که واقعا جالبه و کار باهاش راحت) ، طراح حتی باید مدل ها رو بشناسه، یا حتی در روش باز هم توصیه شده ، کلاس های سی شارپی به عنوان viewModel تعریف کنه و view رو با اونها تشکیل بده. در حالیکه در مرحله کدنویسی دیگه به عناصر صفحه وابستگی وجود نداره.
منظورم اینه که توی هرکدوم به نظرم وابستگی وجود داره، ولی تو مراحل متفاوت.اینطوره؟
تایپوگرافی
هدف از تایپوگرافی، چیدمان متن به نحوی است که واضح، خوانا و مشخص باشد؛ همچنین مباحث زیبایی ارائه را نیز به آن اضافه کنید. برای مثال تنظیم فاصله بین حروف و کلمات، فاصله بین خطوط و یا رعایت یک سری نسبتهای ویژه مانند نسبت طلایی جهت دعوت خواننده به مطالعه مطالب، بجای فراری دادن او، در مباحث تایپوگرافی رعایت میشوند. خوشبختانه Twitter Bootstrap به همراه یک سری تنظیمات تایپوگرافی پیش فرض است که در ادامه آنها را مرور خواهیم کرد.
پیش فرضهای ابتدایی آنرا مانند قلم با اندازه 14px، قلم پیش فرض Helvetica، فاصله بین خطوط و رنگ متن را در فایل bootstrap.css میتوانید مشاهده کنید:
body { margin: 0;"Helvetica Neue", Helvetica, Arial, sans-serif; color: #333333; background-color: #ffffff; }
<div class="row-fluid"> <div class="span12"> <h1> سرتیتر 1 </h1> <h2> سرتیتر 2 </h2> <h3> سرتیتر 3 </h3> <h4> سرتیتر 4 </h4> <h5> سرتیتر 5 </h5> <h6> سرتیتر 6 </h6> </div> </div> |
<div class="row-fluid"> <div class="span12"> <p class="lead"> تیتر</p> <p> متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن متن </p> <small>اندازه کوچک</small> <strong>متن ضخیم</strong> <em>متن ایتالیک</em> </div> </div>
روش دیگر جلب توجه به یک متن در bootstrap، استفاده از کلاسهای text مانند text-error و امثال آن میباشد:
<div class="row-fluid"> <div class="span12"> <p class="muted"> متن غیرفعال</p> <p class="text-warning"> نمایش اخطار به کاربر</p> <p class="text-error"> نمایش خطا به کاربر</p> <p class="text-info"> نمایش اطلاعات به کاربر</p> <p class="text-success"> نمایش موفقیت آمیز بودن عملیات</p> </div> </div> |
<div class="row-fluid"> <div class="span12"> <p> My <abbr title="منظور واحد پردازش مرکزی است"> CPU</abbr> has N Cores. </p> </div> </div> |
<div class="row-fluid"> <div class="span12"> <address> وحید نصیری <br /> ایران، تهران <br /> <abbr title="Phone"> P:</abbr> 12345678 <br /> <abbr title="Cell"> C:</abbr> 12345678 </address> </div> </div> |
<div class="row-fluid"> <div class="span12"> <blockquote> <p> جهت نمایش نقل قول </p> <small><cite title="کاربر شماره 2">از شخصی</cite> </small> </blockquote> </div> </div> |
<div class="row-fluid"> <div class="span6"> <ul class="unstyled"> <li>یک </li> <li>دو </li> <li>سه </li> </ul> </div> <div class="span6"> <ol> <li>یک </li> <li>دو </li> <li>سه </li> </ol> </div> </div> |
<div class="row-fluid"> <div class="span12"> <dl class="dl-horizontal"> <dt>عنوان</dt> <dd> توضیحات بلند در اینجا</dd> </dl> </div> </div> |
<div class="row-fluid"> <div class="span6"> <code>inline code</code> </div> <div class="span6"> <pre class="pre-scrollable"> code code code </pre> </div> </div> |
استفاده از جداول و تاثیر bootstrap بر آنها
در ادامه کدهای یک جدول متداول را که مزین شدهاست به کلاسهای bootstrap ملاحظه میکنید:
<div class="container-fluid"> <div class="row-fluid"> <div class="span12"> <table class="table table-striped table-hover table-bordered table-condensed"> <caption> عنوانی خاص در اینجا</caption> <thead> <tr> <th> ستون یک </th> <th> ستون دو </th> <th> ستون سه </th> </tr> </thead> <tbody> <tr class="error"> <td> 1 متن یک </td> <td> 1 متن دو </td> <td> 1 متن سه </td> </tr> <tr> <td> 2 متن یک </td> <td> 2 متن دو </td> <td> 2 متن سه </td> </tr> <tr> <td> 3 متن یک </td> <td> 3 متن دو </td> <td> 3 متن سه </td> </tr> </tbody> </table> </div> </div> </div>
در اینجا ذکر caption اختیاری است. وجود thead و tbody به تشخیص هدر و ردیفها جهت اعمال شیوهنامههای متناظر کمک میکنند. همچنین کلاسهای ذیل نیز به جدول اعمال شدهاند:
table: سبب میشود تا تنظیمات ابتدایی bootstrap به جدول طراحی شده، اعمال شوند.
table-striped: رنگ زمینه سطرها را یک در میان تغییر میدهد.
table-hover: سبب میشود تا با عبور اشارهگر ماوس از روی سطرها، رنگ زمینه آنها تغییر کنند.
table-bordered: حاشیهای را به جدول و ردیفها اعمال میکنند. همچنین سبب نمایش گوشههای گرد نیز میشود.
table-condensed: اندکی padding اعمال شده به سلولهای جداول را کاهش میدهد و جدول را فشردهتر میکند.
در جدول فوق، کلاس نمونه error به یک tr نیز اعمال شدهاست تا اثر آنرا بر روی یک ردیف بهتر بتوان ملاحظه کرد.
طراحی فرمها و تاثیر bootstrap بر آنها
قرار دادن برچسبها و عناصر صفحه، به نحوی کاربرپسند و دلپذیر، نیاز به رعایت یک سری اصول تایپوگرافی و طراحی ویژه دارد که این موارد نیز در bootstrap گنجانده شدهاند.
1) فرمهای عمودی
<div class="container-fluid"> <div class="row-fluid"> <div class="span12"> <form action="/signup" method="post"> <fieldset> <legend>ثبت نام</legend> <label> ایمیل:</label> <input name="email" type="text" /> <label> نام:</label> <input name="name" type="text" /> </fieldset> </form> </div> </div> </div> |
همانطور که ملاحظه میکنید، کلیه عناصر را به صورت یک پشته عمودی در صفحه قرار دادهاست و نکته مهم اینجا است که هیچگونه شیوه نامه خاص و اضافهتری به فرم فوق از طرف ما اعمال نشده است.
نکته: اگر میخواهید همان حالت پیش فرض مرورگر، یعنی قرار دادن تمام عناصر در پشت سر هم را فعال کنید، تنها کافی است کلاس form-inline را به form تعریف شده فوق اضافه نمائید.
2) نکاتی در مورد checkboxes و radio buttons
در حالت پیش فرض، با تعریف یک label و checkbox، برچسب متناظر با آن اندکی بالاتر از checkbox قرار گرفته شده در صفحه ظاهر میشود. برای رفع این مشکل تنها کافی است کلاس checkbox به label اعمال شود (و برای radio button از کلاس radio استفاده خواهد شد):
<label class="checkbox"> <input type="checkbox" name="isMale" /> مذکر</label>
نکته: برای قرار دادن چندین checkbox یا radio button در یک سطر (با توجه به حالت چیدمان عمودی پیش فرض فرمها)، ابتدا آنها را داخل یک div قرار دهید. سپس به تمام checkboxها یا radio buttonها کلاس inline را نیز اضافه نمائید. برای مثال:
<div> <label class="radio inline"> <input type="radio" name="isMale" /> مذکر</label> <label class="radio inline"> <input type="radio" name="isFemale" /> مؤنث</label> </div> |
4) تعیین اندازه فیلدها
اگر علاقمند هستید که یک textarea کل عرض صفحه را به خود اختصاص دهد، از کلاس input-block-level استفاده کنید.
5) فرمهای جستجو
<form class="search-form"> <fieldset> <legend>جستجو</legend> <input type="search" class="search-query" /> <button type="submit" class="btn" >بیاب</button> </fieldset> </form> |
- کلاس فرم بهتر است search-form تعیین شود
- نوع input بهتر است search وارد شود
- کلاس input جستجو نیز search-query انتخاب گردد
همانطور که ملاحظه میکنید، در این حالت گوشههای جعبه متنی جستجو، نسبت به حالتهای معمولی آنها گرد شده است. کلاس دکمه نیز btn درنظر گرفته شده است تا حالت ویژه دکمههای bootstrap را پیدا کند.
6) فرمهای افقی
تا اینجا، با فرمهای حالت پیش فرض یا فرمهایی که عناصر را به صورت پشتهای عمودی بر روی یکدیگر قرار میدهند، آشنا شدیم. حالت متداول دیگر طراحی فرمها، حالت افقی است. به این معنا که در هر سطر، یک برچسب و یک المان قرار گیرند، بجای اینکه ابتدا برچسب نمایش داده شود و در سطر بعدی، المان مرتبط با آن. Bootstrap برای طراحی بدون استفاده از جداول این نوع فرمها نیز تنظیمات خاصی را تدارک دیدهاست.
<form class="form-horizontal" action="/signup" method="post"> <fieldset> <legend>ثبت نام</legend> <div class="control-group"> <label class="control-label"> ایمیل:</label> <div class="controls"> <input name="email" type="text" /> </div> </div> <div class="control-group"> <label class="control-label"> نام:</label> <div class="controls"> <input name="name" type="text" /> </div> </div> <div class="control-group"> <div class="controls"> <label class="radio inline"> <input type="radio" name="isMale" /> مذکر</label> <label class="radio inline"> <input type="radio" name="isFemale" /> مؤنث</label> </div> </div> </fieldset> </form> |
- کلاس form-horizontal را به فرم جاری اضافه کنید.
- هر سطر مورد نظر را در div ایی با کلاس control-group محصور نمائید.
- به برچسبها، کلاس control-label را انتساب دهید.
- کنترلهای مدنظر را در div ایی با کلاس controls محصور کنید.
هر چند این روش نیاز به اندکی HTML نویسی دارد، اما بسیاری به این نوع فرمها بیشتر علاقمند هستند تا فرمهای عمودی ابتدای بحث.
7) جلب توجه کاربران به فیلدها برای نمایش خطاهای اعتبارسنجی
<div class="control-group error"> <label class="control-label"> ایمیل:</label> <div class="controls"> <input name="email" type="text" /> <span class="help-block">لطفا ایمیل را با فرمت صحیحی وارد نمائید</span> </div> </div> |
8) توسعه فیلدهای استاندارد
<div class="input-prepend input-append"> <span dir="ltr" class="add-on">.00</span> <input dir="ltr" type="text" /> <span class="add-on">ریال</span> </div> |
9) HTML Helpers مخصوص ASP.NET MVC برای کار با bootstrap
نکاتی را که در اینجا مطرح شدند، اگر علاقمند بودید که به شکلی strongly typed در ASP.NET MVC اعمال کنید، میتوان به پروژههایی مانند TwitterBootstrapMvc مراجعه کرد. تعداد این نوع پروژهها هم روز به روز بیشتر میشوند:
https://twitterbootstrapmvc.codeplex.com/
https://mvc4bootstaphelper.codeplex.com/
https://github.com/erichexter/twitter.bootstrap.mvc
http://bootstraphelpers.codeplex.com/
تنظیمات خاص دکمهها در حین استفاده از Twitter Bootstrap
در مثال ذیل، کلاسهای مرتبط با تزئین دکمهها را توسط bootstrap، ملاحظه میکنید:
<div class="container-fluid"> <div class="row-fluid"> <div class="span12"> <table class="table table-striped table-hover table-bordered table-condensed"> <thead> <tr> <th> دکمه </th> <th> لینک </th> <th> کلاس بکار گرفته شده </th> </tr> </thead> <tbody> <tr> <td> <button class="btn"> default</button> </td> <td> <a href="#" class="btn">default</a> </td> <td> <code>btn</code> </td> </tr> <tr> <td> <button class="btn btn-primary"> primary</button> </td> <td> <a href="#" class="btn btn-primary">primary</a> </td> <td> <code>btn btn-primary</code> </td> </tr> <tr> <td> <button class="btn btn-info"> info</button> </td> <td> <a href="#" class="btn btn-info">info</a> </td> <td> <code>btn btn-info</code> </td> </tr> <tr> <td> <button class="btn btn-success"> success</button> </td> <td> <a href="#" class="btn btn-success">success</a> </td> <td> <code>btn btn-success</code> </td> </tr> <tr> <td> <button class="btn btn-warning"> warning</button> </td> <td> <a href="#" class="btn btn-warning">warning</a> </td> <td> <code>btn btn-warning</code> </td> </tr> <tr> <td> <button class="btn btn-danger"> danger</button> </td> <td> <a href="#" class="btn btn-danger">danger</a> </td> <td> <code>btn btn-danger</code> </td> </tr> <tr> <td> <button class="btn btn-inverse"> inverse</button> </td> <td> <a href="#" class="btn btn-inverse">inverse</a> </td> <td> <code>btn btn-inverse</code> </td> </tr> <tr> <td> <button class="btn btn-link"> link</button> </td> <td> <a href="#" class="btn btn-link">link</a> </td> <td> <code>btn btn-link</code> </td> </tr> <tr> <td> <button class="btn btn-primary btn-large"> large</button> </td> <td> <a href="#" class="btn btn-primary btn-large">large</a> </td> <td> <code>btn btn-primary btn-large</code> </td> </tr> <tr> <td> <button class="btn btn-primary btn-small"> small</button> </td> <td> <a href="#" class="btn btn-primary btn-small">small</a> </td> <td> <code>btn btn-primary btn-small</code> </td> </tr> <tr> <td> <button class="btn btn-primary btn-mini"> mini</button> </td> <td> <a href="#" class="btn btn-primary btn-mini">mini</a> </td> <td> <code>btn btn-primary btn-mini</code> </td> </tr> <tr> <td> <button class="btn btn-primary btn-block"> block</button> </td> <td> <a href="#" class="btn btn-primary btn-block">block</a> </td> <td> <code>btn btn-primary btn-block</code> </td> </tr> <tr> <td> <button class="btn btn-primary disabled"> disabled</button> </td> <td> <a href="#" class="btn btn-primary disabled">disabled</a> </td> <td> <code>btn btn-primary disabled</code> </td> </tr> </tbody> </table> </div> </div> </div>
همانطور که ملاحظه کردید، الزامی ندارد که این کلاسها را حتما به دکمهها اعمال کرد. برای نمونه میتوان از یک span یا لینک نیز برای تعریف دکمهها بهره جست. برای یکپارچه سازی چنین دکمههایی (که در اصل دکمه نیستند) با ASP.NET MVC میتوان به مطلب تکمیلی «استفاده از دکمههای CSS توئیتر در ASP.NET MVC» مراجعه نمود.
یک نکته: اگر علاقمند هستید که تعدادی دکمه را به شکل یک toolbar نمایش دهید، آنها را در یک div محصور کرده و کلاس btn-group را به آن div اعمال نمائید.
کار با تصاویر و آیکونها در Twitter Bootstrap
کلاسهایی مانند img-rounded، img-circle، img-polaroid با اعمال به یک تصویر، سبب گردن شدن گوشههای آن، نمایش دایرهای و یا نمایش به همراه حاشیه یک تصویر خواهند شد.
Twitter Bootstrap به همراه صدها آیکون ارائه شده است. این آیکونها توسط glyphicons.com ایجاد شدهاند. روشی که برای استفاده از آنها توصیه شده است، استفاده از تگ i میباشد. برای مثال:
<i class="icon-music"></i>icon-music
برای مشاهده لیست کلاسهای قابل استفاده، کلمه icon-glass را در فایل bootstrap.css جستجو نمائید؛ تا شروع مدخل مرتبط با آیکونها را بتوانید مشاهده نمائید.
رنگ پیش فرض این آیکونها مشکی است. اگر علاقمند بودید که آنها را برای مثال با رنگ سفید نمایش دهید فقط کافی است کلاس icon-white را پس از کلاس آیکون مدنظر،ذکر کرد:
<i class="icon-music icon-white"></i>icon-music
امکان اعمال این آیکونها به دکمهها نیز وجود دارد. برای مثال:
<button class="btn"> <i class="icon-music"></i>دکمه</button>
<div class="input-prepend"> <span class="add-on"><i class="icon-envelope"></i></span> <input type="email" dir="ltr" /> </div> |
<PackageReference Include="Meziantou.Analyzer" Version="1.0.708"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference>
<PropertyGroup> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> <CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors> <RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild> <RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis> </PropertyGroup>
# MA0051 : Method is too long. maximum allowed: 60. dotnet_diagnostic.MA0051.severity = suggestion
تیم ASP.NET Identity پروژه نمونه ای را فراهم کرده است که میتواند بعنوان نقطه شروعی برای اپلیکیشنهای MVC استفاده شود. پیکربندیهای لازم در این پروژه انجام شدهاند و برای استفاده از فریم ورک جدید آماده است.
شروع به کار : پروژه نمونه را توسط NuGet ایجاد کنید
برای شروع یک پروژه ASP.NET خالی ایجاد کنید (در دیالوگ قالبها گزینه Empty را انتخاب کنید). سپس کنسول Package Manager را باز کرده و دستور زیر را اجرا کنید.
PM> Install-Package Microsoft.AspNet.Identity.Samples -Pre
پس از اینکه NuGet کارش را به اتمام رساند باید پروژه ای با ساختار متداول پروژههای ASP.NET MVC داشته باشید. به تصویر زیر دقت کنید.
همانطور که میبینید ساختار پروژه بسیار مشابه پروژههای معمول MVC است، اما آیتمهای جدیدی نیز وجود دارند. فعلا تمرکز اصلی ما روی فایل IdentityConfig.cs است که در پوشه App_Start قرار دارد.
اگر فایل مذکور را باز کنید و کمی اسکرول کنید تعاریف دو کلاس سرویس را مشاهده میکنید: EmailService و SmsService.
public class EmailService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Plug in your email service here to send an email. return Task.FromResult(0); } } public class SmsService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Plug in your sms service here to send a text message. return Task.FromResult(0); } }
اگر دقت کنید هر دو کلاس قرارداد IIdentityMessageService را پیاده سازی میکنند. میتوانید از این قرارداد برای پیاده سازی سرویسهای اطلاع رسانی ایمیلی، پیامکی و غیره استفاده کنید. در ادامه خواهیم دید چگونه این دو سرویس را بسط دهیم.
یک حساب کاربری مدیریتی پیش فرض ایجاد کنید
پیش از آنکه بیشتر جلو رویم نیاز به یک حساب کاربری در نقش مدیریتی داریم تا با اجرای اولیه اپلیکیشن در دسترس باشد. کلاسی بنام ApplicationDbInitializer در همین فایل وجود دارد که هنگام اجرای اولیه و یا تشخیص تغییرات در مدل دیتابیس، اطلاعاتی را Seed میکند.
public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext> { protected override void Seed(ApplicationDbContext context) { InitializeIdentityForEF(context); base.Seed(context); } //Create User=Admin@Admin.com with password=Admin@123456 in the Admin role public static void InitializeIdentityForEF(ApplicationDbContext db) { var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>(); const string name = "admin@admin.com"; const string password = "Admin@123456"; const string roleName = "Admin"; //Create Role Admin if it does not exist var role = roleManager.FindByName(roleName); if (role == null) { role = new IdentityRole(roleName); var roleresult = roleManager.Create(role); } var user = userManager.FindByName(name); if (user == null) { user = new ApplicationUser { UserName = name, Email = name }; var result = userManager.Create(user, password); result = userManager.SetLockoutEnabled(user.Id, false); } // Add user admin to Role Admin if not already added var rolesForUser = userManager.GetRoles(user.Id); if (!rolesForUser.Contains(role.Name)) { var result = userManager.AddToRole(user.Id, role.Name); } } }
تایید حسابهای کاربری : چگونه کار میکند
بدون شک با تایید حسابهای کاربری توسط ایمیل آشنا هستید. حساب کاربری ای ایجاد میکنید و ایمیلی به آدرس شما ارسال میشود که حاوی لینک فعالسازی است. با کلیک کردن این لینک حساب کاربری شما تایید شده و میتوانید به سایت وارد شوید.
اگر به کنترلر AccountController در این پروژه نمونه مراجعه کنید متد Register را مانند لیست زیر مییابید.
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Register(RegisterViewModel model) { if (ModelState.IsValid) { var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; var result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); var callbackUrl = Url.Action( "ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); await UserManager.SendEmailAsync( user.Id, "Confirm your account", "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>"); ViewBag.Link = callbackUrl; return View("DisplayEmail"); } AddErrors(result); } // If we got this far, something failed, redisplay form return View(model); }
public static ApplicationUserManager Create( IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) { var manager = new ApplicationUserManager( new UserStore<ApplicationUser>( context.Get<ApplicationDbContext>())); // Configure validation logic for usernames manager.UserValidator = new UserValidator<ApplicationUser>(manager) { AllowOnlyAlphanumericUserNames = false, RequireUniqueEmail = true }; // Configure validation logic for passwords manager.PasswordValidator = new PasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = true, RequireDigit = true, RequireLowercase = true, RequireUppercase = true, }; // Configure user lockout defaults manager.UserLockoutEnabledByDefault = true; manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5); manager.MaxFailedAccessAttemptsBeforeLockout = 5; // Register two factor authentication providers. This application // uses Phone and Emails as a step of receiving a code for verifying the user // You can write your own provider and plug in here. manager.RegisterTwoFactorProvider( "PhoneCode", new PhoneNumberTokenProvider<ApplicationUser> { MessageFormat = "Your security code is: {0}" }); manager.RegisterTwoFactorProvider( "EmailCode", new EmailTokenProvider<ApplicationUser> { Subject = "SecurityCode", BodyFormat = "Your security code is {0}" }); manager.EmailService = new EmailService(); manager.SmsService = new SmsService(); var dataProtectionProvider = options.DataProtectionProvider; if (dataProtectionProvider != null) { manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>( dataProtectionProvider.Create("ASP.NET Identity")); } return manager; }
در قطعه کد بالا کلاسهای EmailService و SmsService روی وهله ApplicationUserManager تنظیم میشوند.
manager.EmailService = new EmailService(); manager.SmsService = new SmsService();
درست در بالای این کدها میبینید که چگونه تامین کنندگان احراز هویت دو مرحله ای (مبتنی بر ایمیل و پیامک) رجیستر میشوند.
// Register two factor authentication providers. This application // uses Phone and Emails as a step of receiving a code for verifying the user // You can write your own provider and plug in here. manager.RegisterTwoFactorProvider( "PhoneCode", new PhoneNumberTokenProvider<ApplicationUser> { MessageFormat = "Your security code is: {0}" }); manager.RegisterTwoFactorProvider( "EmailCode", new EmailTokenProvider<ApplicationUser> { Subject = "SecurityCode", BodyFormat = "Your security code is {0}" });
تایید حسابهای کاربری توسط ایمیل و احراز هویت دو مرحله ای توسط ایمیل و/یا پیامک نیاز به پیاده سازی هایی معتبر از قراردارد IIdentityMessageService دارند.
پیاده سازی سرویس ایمیل توسط ایمیل خودتان
پیاده سازی سرویس ایمیل نسبتا کار ساده ای است. برای ارسال ایمیلها میتوانید از اکانت ایمیل خود و یا سرویس هایی مانند SendGrid استفاده کنید. بعنوان مثال اگر بخواهیم سرویس ایمیل را طوری پیکربندی کنیم که از یک حساب کاربری Outlook استفاده کند، مانند زیر عمل خواهیم کرد.
public class EmailService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Credentials: var credentialUserName = "yourAccount@outlook.com"; var sentFrom = "yourAccount@outlook.com"; var pwd = "yourApssword"; // Configure the client: System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient("smtp-mail.outlook.com"); client.Port = 587; client.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network; client.UseDefaultCredentials = false; // Creatte the credentials: System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(credentialUserName, pwd); client.EnableSsl = true; client.Credentials = credentials; // Create the message: var mail = new System.Net.Mail.MailMessage(sentFrom, message.Destination); mail.Subject = message.Subject; mail.Body = message.Body; // Send: return client.SendMailAsync(mail); } }
پیاده سازی سرویس ایمیل با استفاده از SendGrid
سرویسهای ایمیل متعددی وجود دارند اما یکی از گزینههای محبوب در جامعه دات نت SendGrid است. این سرویس API قدرتمندی برای زبانهای برنامه نویسی مختلف فراهم کرده است. همچنین یک Web API مبتنی بر HTTP نیز در دسترس است. قابلیت دیگر اینکه این سرویس مستقیما با Windows Azure یکپارچه میشود.
می توانید در سایت SendGrid یک حساب کاربری رایگان بعنوان توسعه دهنده بسازید. پس از آن پیکربندی سرویس ایمیل با مرحله قبل تفاوت چندانی نخواهد داشت. پس از ایجاد حساب کاربری توسط تیم پشتیبانی SendGrid با شما تماس گرفته خواهد شد تا از صحت اطلاعات شما اطمینان حاصل شود. برای اینکار چند گزینه در اختیار دارید که بهترین آنها ایجاد یک اکانت ایمیل در دامنه وب سایتتان است. مثلا اگر هنگام ثبت نام آدرس وب سایت خود را www.yourwebsite.com وارد کرده باشید، باید ایمیلی مانند info@yourwebsite.com ایجاد کنید و توسط ایمیل فعالسازی آن را تایید کند تا تیم پشتیبانی مطمئن شود صاحب امتیاز این دامنه خودتان هستید.
تنها چیزی که در قطعه کد بالا باید تغییر کند اطلاعات حساب کاربری و تنظیمات SMTP است. توجه داشته باشید که نام کاربری و آدرس فرستنده در اینجا متفاوت هستند. در واقع میتوانید از هر آدرسی بعنوان آدرس فرستنده استفاده کنید.
public class EmailService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { // Credentials: var sendGridUserName = "yourSendGridUserName"; var sentFrom = "whateverEmailAdressYouWant"; var sendGridPassword = "YourSendGridPassword"; // Configure the client: var client = new System.Net.Mail.SmtpClient("smtp.sendgrid.net", Convert.ToInt32(587)); client.Port = 587; client.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network; client.UseDefaultCredentials = false; // Creatte the credentials: System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(credentialUserName, pwd); client.EnableSsl = true; client.Credentials = credentials; // Create the message: var mail = new System.Net.Mail.MailMessage(sentFrom, message.Destination); mail.Subject = message.Subject; mail.Body = message.Body; // Send: return client.SendMailAsync(mail); } }
آزمایش تایید حسابهای کاربری توسط سرویس ایمیل
ابتدا اپلیکیشن را اجرا کنید و سعی کنید یک حساب کاربری جدید ثبت کنید. دقت کنید که از آدرس ایمیلی زنده که به آن دسترسی دارید استفاده کنید. اگر همه چیز بدرستی کار کند باید به صفحه ای مانند تصویر زیر هدایت شوید.
همانطور که مشاهده میکنید پاراگرافی در این صفحه وجود دارد که شامل لینک فعالسازی است. این لینک صرفا جهت تسهیل کار توسعه دهندگان درج میشود و هنگام توزیع اپلیکیشن باید آن را حذف کنید. در ادامه به این قسمت باز میگردیم. در این مرحله ایمیلی حاوی لینک فعالسازی باید برای شما ارسال شده باشد.
پیاده سازی سرویس SMS
برای استفاده از احراز هویت دو مرحله ای پیامکی نیاز به یک فراهم کننده SMS دارید، مانند Twilio . مانند SendGrid این سرویس نیز در جامعه دات نت بسیار محبوب است و یک C# API قدرتمند ارائه میکند. میتوانید حساب کاربری رایگانی بسازید و شروع به کار کنید.
پس از ایجاد حساب کاربری یک شماره SMS، یک شناسه SID و یک شناسه Auth Token به شما داده میشود. شماره پیامکی خود را میتوانید پس از ورود به سایت و پیمایش به صفحه Numbers مشاهده کنید.
شناسههای SID و Auth Token نیز در صفحه Dashboard قابل مشاهده هستند.
اگر دقت کنید کنار شناسه Auth Token یک آیکون قفل وجود دارد که با کلیک کردن روی آن شناسه مورد نظر نمایان میشود.
حال میتوانید از سرویس Twilio در اپلیکیشن خود استفاده کنید. ابتدا بسته NuGet مورد نیاز را نصب کنید.
PM> Install-Package Twilio
public class SmsService : IIdentityMessageService { public Task SendAsync(IdentityMessage message) { string AccountSid = "YourTwilioAccountSID"; string AuthToken = "YourTwilioAuthToken"; string twilioPhoneNumber = "YourTwilioPhoneNumber"; var twilio = new TwilioRestClient(AccountSid, AuthToken); twilio.SendSmsMessage(twilioPhoneNumber, message.Destination, message.Body); // Twilio does not return an async Task, so we need this: return Task.FromResult(0); } }
حال که سرویسهای ایمیل و پیامک را در اختیار داریم میتوانیم احراز هویت دو مرحله ای را تست کنیم.
آزمایش احراز هویت دو مرحله ای
پروژه نمونه جاری طوری پیکربندی شده است که احراز هویت دو مرحله ای اختیاری است و در صورت لزوم میتواند برای هر کاربر بصورت جداگانه فعال شود. ابتدا توسط حساب کاربری مدیر، یا حساب کاربری ای که در قسمت تست تایید حساب کاربری ایجاد کرده اید وارد سایت شوید. سپس در سمت راست بالای صفحه روی نام کاربری خود کلیک کنید. باید صفحه ای مانند تصویر زیر را مشاهده کنید.
در این قسمت باید احراز هویت دو مرحله ای را فعال کنید و شماره تلفن خود را ثبت نمایید. پس از آن یک پیام SMS برای شما ارسال خواهد شد که توسط آن میتوانید پروسه را تایید کنید. اگر همه چیز بدرستی کار کند این مراحل چند ثانیه بیشتر نباید زمان بگیرد، اما اگر مثلا بیش از 30 ثانیه زمان برد احتمالا اشکالی در کار است.
حال که احراز هویت دو مرحله ای فعال شده از سایت خارج شوید و مجددا سعی کنید به سایت وارد شوید. در این مرحله یک انتخاب به شما داده میشود. میتوانید کد احراز هویت دو مرحله ای خود را توسط ایمیل یا پیامک دریافت کنید.
پس از اینکه گزینه خود را انتخاب کردید، کد احراز هویت دو مرحله ای برای شما ارسال میشود که توسط آن میتوانید پروسه ورود به سایت را تکمیل کنید.
حذف میانبرهای آزمایشی
همانطور که گفته شد پروژه نمونه شامل میانبرهایی برای تسهیل کار توسعه دهندگان است. در واقع اصلا نیازی به پیاده سازی سرویسهای ایمیل و پیامک ندارید و میتوانید با استفاده از این میانبرها حسابهای کاربری را تایید کنید و کدهای احراز هویت دو مرحله ای را نیز مشاهده کنید. اما قطعا این میانبرها پیش از توزیع اپلیکیشن باید حذف شوند.
بدین منظور باید نماها و کدهای مربوطه را ویرایش کنیم تا اینگونه اطلاعات به کلاینت ارسال نشوند. اگر کنترلر AccountController را باز کنید و به متد ()Register بروید با کد زیر مواجه خواهید شد.
if (result.Succeeded) { var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>"); // This should not be deployed in production: ViewBag.Link = callbackUrl; return View("DisplayEmail"); } AddErrors(result);
نمایی که این متد باز میگرداند یعنی DisplayEmail.cshtml نیز باید ویرایش شود.
@{ ViewBag.Title = "DEMO purpose Email Link"; } <h2>@ViewBag.Title.</h2> <p class="text-info"> Please check your email and confirm your email address. </p> <p class="text-danger"> For DEMO only: You can click this link to confirm the email: <a href="@ViewBag.Link">link</a> Please change this code to register an email service in IdentityConfig to send an email. </p>
متد دیگری که در این کنترلر باید ویرایش شود ()VerifyCode است که کد احراز هویت دو مرحله ای را به صفحه مربوطه پاس میدهد.
[AllowAnonymous] public async Task<ActionResult> VerifyCode(string provider, string returnUrl) { // Require that the user has already logged in via username/password or external login if (!await SignInHelper.HasBeenVerified()) { return View("Error"); } var user = await UserManager.FindByIdAsync(await SignInHelper.GetVerifiedUserIdAsync()); if (user != null) { ViewBag.Status = "For DEMO purposes the current " + provider + " code is: " + await UserManager.GenerateTwoFactorTokenAsync(user.Id, provider); } return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl }); }
همانطور که میبینید متغیری بنام Status به ViewBag اضافه میشود که باید حذف شود.
نمای این متد یعنی VerifyCode.cshtml نیز باید ویرایش شود.
@model IdentitySample.Models.VerifyCodeViewModel @{ ViewBag.Title = "Enter Verification Code"; } <h2>@ViewBag.Title.</h2> @using (Html.BeginForm("VerifyCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken() @Html.ValidationSummary("", new { @class = "text-danger" }) @Html.Hidden("provider", @Model.Provider) <h4>@ViewBag.Status</h4> <hr /> <div class="form-group"> @Html.LabelFor(m => m.Code, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.Code, new { @class = "form-control" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <div class="checkbox"> @Html.CheckBoxFor(m => m.RememberBrowser) @Html.LabelFor(m => m.RememberBrowser) </div> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" class="btn btn-default" value="Submit" /> </div> </div> }
در این فایل کافی است ViewBag.Status را حذف کنید.
از تنظیمات ایمیل و SMS محافظت کنید
در مثال جاری اطلاعاتی مانند نام کاربری و کلمه عبور، شناسههای SID و Auth Token همگی در کد برنامه نوشته شده اند. بهتر است چنین مقادیری را بیرون از کد اپلیکیشن نگاه دارید، مخصوصا هنگامی که پروژه را به سرویس کنترل ارسال میکند (مثلا مخازن عمومی مثل GitHub). بدین منظور میتوانید یکی از پستهای اخیر را مطالعه کنید.
ویژگیهای مورد بحث شامل موارد زیر میباشند:
• ویرایش TextBoxها در محیط Designer
• Copy و Paste کردن Styleها از یک کنترل به کنترل دیگر
• قالب بندی شرطی
• پیمایش و تغییر اندازه گزارش و آیتمهای آن
• تغییر اندازه بخشهای مختلف گزارش نظیر Page Header ، Detail و ...
• افزودن TextBox Shape و PictureBox درون Designer گزارش
• استفاده از پنجره Data Explorer
• استفاده از پنجره Report Explorer
در ادامه به بررسی موارد ذکر شده جهت طراحی گزارش میپردازیم.
1. جهت تغییر محتوای یک TextBox میتوان روی آن دوبار کلیک نمود. پس از آن TextBox به حالت ویرایش میرود و میتوان متن درون آن را به سادگی تغییر داد.این کار در محیط Designer انجام میشود و نیازی نیست برای تغییر محتوای TextBox به پنجره Properties بروید و متن آن را تغییر دهید.
2. در محیط Designer به راحتی میتوانید گزارش خود را Zoom نمایید.این کار توسط ComboBox مربوطه در پایین سمت چپ Designer انجام میشود.
3. اگر قرار باشد TextBox جدیدی به گزارش خود اضافه نمایید، کافی است آن را از بخش ToolBox به محیط گزارش بکشید و سپس به چینش آن بپردازید.
4. در محیط طراح گزارش Telerik میتوانید به راحتی یک قالب بندی و یا Style را از کنترلی به کنترل دیگر کپی نمایید و در وقت خود جهت طراحی گزارش صرفه جویی نمایید. برای انجام اینکار کافی است روی کنترلی که قرار است Style آن را کپی نمایید راست کلیک نموده و پس از آن از منوی ظاهر شده گزینه Copy Style را انتخاب نمایید. در ادامه میتوانید کنترل و یا کنترل هایی که قرار است قالب بندی را به آنها اعمال کنید انتخاب نموده ، روی آنها راست کلیک نمایید و گزینه Paste Style را انتخاب کنید.با این کار Style ی که در مرحله قبل از کنترلی دیگر کپی کرده بودید به کنترل یا کنترلهای انتخاب شده اعمال میشود.
5. یکی دیگر از امکانات Telerik Reporting امکان قالب بندی شرطی (Conditional Formating) میباشد. یعنی Style یک کنترل توسط شرطها تعیین میشود. برای مثال میتوانیم بگوییم که اگر مقدار فروش بیشتر از مبلغ خاصی بود ، عدد نمایش داده شده با رنگ سبز نمایش داده شود و یا اینکه از فونت و یا اندازه دیگری جهت نمایش آن استفاده شود (به طور کلی با توجه به شرطهای تعیین شده نمایش آن کنترل با یک Style متفاوت صورت گیرد). در قسمتهای آینده به بررسی کامل این قابلیت نیز خواهیم پرداخت.
6. یکی از امکاناتی که در هنگام طراحی گزارش در اختیار ما قرار میگیرد پنجره Data Explorer میباشد. توسط این پنجره میتوان فیلدهای یک منبع داده (DataSource) را مشاهده نمود و برای اینکه بتوان از آنها در محیط طراحی استفاده کرد بر روی محیط طراحی درگ نمود. DataSourceها انواع مختلفی دارند که در قسمت اول این آموزش به معرفی آنها پرداختیم و از نمونه Sql آن نیز جهت طراحی یک گزارش ساده استفاده کردیم. در ادامه نیز با این موارد بیشتر آشنا خواهید شد.در تصویر زیر نحوهی درگ کردن یک فیلد تصویر را از پنجره Data Explorer مشاهده مینمایید.
7.
یکی دیگر از اجزای Reporting پنجرهی Report Explorer میباشد. توسط این
پنجره میتوان دسترسی سریعی به اجزای درون گزارش داشت. برای مثال میتوان
به راحتی یک بخش درون گزارش را انتخاب نمود و در پنجرهی Properties
تغییراتی در آن اعمال نمود.
ادامه دارد ...