وضعیت دورکاری در سال 2020
وضعیت CSS در سال 2019
بررسی Tuples در C# 7.0
شما برای این کار زمانی که میخواهید Script از Database خود تهیه کنید با پس از مشخص کردن Objectهای مورد نظر از قیبل (Table,SP,..) باید گزینه Advance رو کلیک کنید و در پنجره پیش رو کلیهی تنظیماتی که در خصوص Objectها وجود دارد را میتوانید انجام دهید .
و در آخر موضوعی که شما مد نظرتان است را میتوانید با True کردن گزینهی مربوط به Index میتوانید کلیدها را هم انتقال بدهید.
انشالله تو این هفته سعی میکنم مقاله ای کامل در باره Script در بانک اطلاعاتی در سایت قرار دهم.
سئوالی بود در خدمتم
.
معرفی قالبهای جدید شروع پروژههای Blazor در دات نت 8 به همراه قسمت Identity
در قسمت دوم این سری، با قالبهای جدید شروع پروژههای Blazor 8x آشنا شدیم و هدف ما در آنجا بیشتر بررسی حالتهای مختلف رندر Blazor در دات نت 8 بود. تمام این قالبها به همراه یک سوئیچ دیگر هم به نام auth-- هستند که توسط آن و با مقدار دهی Individual که به معنای Individual accounts است، میتوان کدهای پیشفرض و ابتدایی Identity UI جدید را نیز به قالب در حال ایجاد، به صورت خودکار اضافه کرد؛ یعنی به صورت زیر:
اجرای قسمتهای تعاملی برنامه بر روی سرور؛ به همراه کدهای Identity:
dotnet new blazor --interactivity Server --auth Individual
اجرای قسمتهای تعاملی برنامه در مرورگر، توسط فناوری وباسمبلی؛ به همراه کدهای Identity:
dotnet new blazor --interactivity WebAssembly --auth Individual
برای اجرای قسمتهای تعاملی برنامه، ابتدا حالت Server فعالسازی میشود تا فایلهای WebAssembly دریافت شوند، سپس فقط از WebAssembly استفاده میکند؛ به همراه کدهای Identity:
dotnet new blazor --interactivity Auto --auth Individual
فقط از حالت SSR یا همان static server rendering استفاده میشود (این نوع برنامهها تعاملی نیستند)؛ به همراه کدهای Identity:
dotnet new blazor --interactivity None --auth Individual
و یا توسط پرچم all-interactive--، که interactive render mode را در ریشهی برنامه قرار میدهد؛ به همراه کدهای Identity:
dotnet new blazor --all-interactive --auth Individual
یک نکته: بانک اطلاعاتی پیشفرض مورد استفادهی در این نوع پروژهها، SQLite است. برای تغییر آن میتوانید از پرچم use-local-db-- هم استفاده کنید تا از LocalDB بجای SQLite استفاده کند.
Identity UI جدید Blazor در دات نت 8، یک بازنویسی کامل است
در نگارشهای قبلی Blazor هم امکان افزودن Identity UI، به پروژههای Blazor وجود داشت (اطلاعات بیشتر)، اما ... آن پیاده سازی، کاملا مبتنی بر Razor pages بود. یعنی کاربر، برای کار با آن مجبور بود از فضای برای مثال Blazor Server خارج شده و وارد فضای جدید ASP.NET Core شود تا بتواند، Identity UI نوشته شدهی با صفحات cshtml. را اجرا کند و به اجزای آن دسترسی پیدا کند (یعنی عملا آن قسمت UI اعتبارسنجی، Blazor ای نبود) و پس از اینکار، مجددا به سمت برنامهی Blazor هدایت شود؛ اما ... این مشکل در دات نت 8 با ارائهی صفحات SSR برطرف شدهاست.
همانطور که در قسمت قبل نیز بررسی کردیم، صفحات SSR، طول عمر کوتاهی دارند و هدف آنها تنها ارسال یک HTML استاتیک به مرورگر کاربر است؛ اما ... دسترسی کاملی را به HttpContext برنامهی سمت سرور دارند و این دقیقا چیزی است که زیر ساخت Identity، برای کار و تامین کوکیهای مورد نیاز خودش، احتیاج دارد. صفحات Identity UI از یک طرف از HttpContext برای نوشتن کوکی لاگین موفقیت آمیز در مرورگر کاربر استفاده میکنند (در این سیستم، هرجائی متدهای XyzSignInAsync وجود دارد، در پشت صحنه و در پایان کار، سبب درج یک کوکی اعتبارسنجی و احراز هویت در مرورگر کاربر نیز خواهد شد) و از طرف دیگر با استفاده از میانافزارهای اعتبارسنجی و احراز هویت ASP.NET Core، این کوکیها را به صورت خودکار پردازش کرده و از آنها جهت ساخت خودکار شیء User قابل دسترسی در این صفحات (شیء context.User.Identity@)، استفاده میکنند.
به همین جهت تمام صفحات Identity UI ارائه شده در Blazor 8x، از نوع SSR هستند و اگر در آنها از فرمی استفاده شده، دقیقا همان فرمهای تعاملی است که در قسمت چهارم این سری بررسی کردیم. یعنی صرفا بخاطر داشتن یک فرم، ضرورتی به استفادهی از جزایر تعاملی Blazor Server و یا Blazor WASM را ندیدهاند و اینگونه فرمها را بر اساس مدل جدید فرمهای تعاملی Blazor 8x پیاده سازی کردهاند. بنابراین این صفحات کاملا یکدست هستند و از ابتدا تا انتها، به صورت یکپارچه بر اساس مدل SSR کار میکنند (که در اصل خیلی شبیه به Razor pages یا Viewهای MVC هستند) و جزیره، جزیرهای، طراحی نشدهاند.
روش دسترسی به HttpContext در صفحات SSR
اگر به کدهای Identity UI قالب آغازین یک پروژه که در ابتدای بحث روش تولید آنها بیان شد، مراجعه کنید، استفاده از یک چنین «پارامترهای آبشاری» را میتوان مشاهده کرد:
@code { [CascadingParameter] public HttpContext HttpContext { get; set; } = default!;
services.TryAddCascadingValue(sp => sp.GetRequiredService<EndpointHtmlRenderer>().HttpContext);
در کدهای Identity UI ارائه شده، از این CascadingParameter برای مثال جهت خروج از برنامه (HttpContext.SignOutAsync) و یا دسترسی به اطلاعات هدرهای رسید (if (HttpMethods.IsGet(HttpContext.Request.Method)))، دسترسی به سرویسها (()<HttpContext.Features.Get<ITrackingConsentFeature)، تامین مقدار Cancellation Token به کمک HttpContext.RequestAborted و یا حتی در صفحهی جدید Error.razor که آن نیز یک صفحهی SSR است، برای دریافت HttpContext?.TraceIdentifier استفادهی مستقیم شدهاست.
نکتهی مهم: باید بهخاطر داشت که فقط و فقط در صفحات SSR است که میتوان به HttpContext به نحو آبشاری فوق دسترسی یافت و همانطور که در قسمت قبل نیز بررسی شد، سایر حالات رندر Blazor از دسترسی به آن، پشتیبانی نمیکنند و اگر چنین پارامتری را تنظیم کردید، نال دریافت میکنید.
بررسی تفاوتهای تنظیمات ابتدایی قالب جدید Identity UI در Blazor 8x با نگارشهای قبلی آن
مطالب و نکات مرتبط با قالب قبلی را در مطلب «Blazor 5x - قسمت 22 - احراز هویت و اعتبارسنجی کاربران Blazor Server - بخش 2 - ورود به سیستم و خروج از آن» میتوانید مشاهده کنید.
در قسمت سوم این سری، روش ارتقاء یک برنامهی قدیمی Blazor Server را به نگارش 8x آن بررسی کردیم و یکی از تغییرات مهم آن، حذف فایلهای cshtml. ای آغاز برنامه و انتقال وظایف آن، به فایل جدید App.razor و انتقال مسیریاب Blazor به فایل جدید Routes.razor است که پیشتر در فایل App.razor نگارشهای قبلی Blazor وجود داشت.
در این نگارش جدید، محتوای فایل Routes.razor به همراه قالب Identity UI به صورت زیر است:
<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> </Router>
اما ... در اینجا با یک نگارش ساده شده سروکار داریم؛ از این جهت که برنامههای جدید، به همراه جزایر تعاملی هم میتوانند باشند و باید بتوان این AuthenticationState را به آنها هم ارسال کرد. به همین جهت مفهوم جدید مقادیر آبشاری سطح ریشه (Root-level cascading values) که پیشتر در این بحث معرفی شد، در اینجا برای معرفی AuthenticationState استفاده شدهاست.
در اینجا کامپوننت جدید FocusOnNavigate را هم مشاهده میکنید. با استفاده از این کامپوننت و براساس CSS Selector معرفی شده، پس از هدایت به یک صفحهی جدید، این المان مشخص شده دارای focus خواهد شد. هدف از آن، اطلاع رسانی به screen readerها در مورد هدایت به صفحهای دیگر است (برای مثال، کمک به نابیناها برای درک بهتر وضعیت جاری).
همچنین در اینجا المان NotFound را هم مشاهده نمیکنید. این المان فقط در برنامههای WASM جهت سازگاری با نگارشهای قبلی، هنوز هم قابل استفادهاست. جایگزین آنرا در قسمت سوم این سری، برای برنامههای Blazor server در بخش «ایجاد فایل جدید Routes.razor و مدیریت سراسری خطاها و صفحات یافت نشده» آن بررسی کردیم.
روش انتقال اطلاعات سراسری اعتبارسنجی یک کاربر به کامپوننتها و جزایر تعاملی واقع در صفحات SSR
مشکل! زمانیکه از ترکیب صفحات SSR و جزایر تعاملی واقع در آن استفاده میکنیم ... جزایر واقع در آنها دیگر این مقادیر آبشاری را دریافت نمیکنند و این مقادیر در آنها نال است. برای حل این مشکل در Blazor 8x، باید مقادیر آبشاری سطح ریشه را (Root-level cascading values) به صورت زیر در فایل Program.cs برنامه ثبت کرد:
builder.Services.AddCascadingValue(sp =>new Preferences { Theme ="Dark" });
builder.Services.AddCascadingAuthenticationState(); builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();
این AuthenticationStateProvider سفارشی جدید هم در فایل اختصاصی IdentityRevalidatingAuthenticationStateProvider.cs پوشهی Identity قالب پروژههای جدید Blazor 8x که به همراه اعتبارسنجی هستند، قابل مشاهدهاست.
یا اگر به قالبهای پروژههای WASM دار مراجعه کنید، تعریف به این صورت تغییر کردهاست؛ اما مفهوم آن یکی است:
builder.Services.AddCascadingAuthenticationState(); builder.Services.AddScoped<AuthenticationStateProvider, PersistingServerAuthenticationStateProvider>();
AuthenticationStateProviderهای سفارشی مانند کلاسهای IdentityRevalidatingAuthenticationStateProvider و PersistingServerAuthenticationStateProvider که در این قالبها موجود هستند، چون به صورت آبشاری کار تامین AuthenticationState را انجام میدهند، به این ترتیب میتوان به شیء context.User.Identity@ در جزایر تعاملی نیز به سادگی دسترسی داشت.
@page "/auth" @using Microsoft.AspNetCore.Authorization @attribute [Authorize] <PageTitle>Auth</PageTitle> <h1>You are authenticated</h1> <AuthorizeView> Hello @context.User.Identity?.Name! </AuthorizeView>
سؤال: چگونه یک پروژهی سمت سرور، اطلاعات اعتبارسنجی خودش را با یک پروژهی WASM سمت کلاینت به اشتراک میگذارد (برای مثال در حالت رندر Auto)؟ این انتقال اطلاعات باتوجه به یکسان نبودن محل اجرای این دو پروژه (یکی بر روی کامپیوتر سرور و دیگری بر روی مرورگر کلاینت، در کامپیوتری دیگر) چگونه انجام میشود؟ این مورد را در قسمت بعد، با معرفی PersistentComponentState و «فیلدهای مخفی» مرتبط با آن، بررسی میکنیم.
@if (!Model.Id.HasValue) { <div class="form-group"> <label>رمز عبور</label> @Html.BootstrapEditorLtrFor(x => x.Password) @Html.ValidationMessageFor(x => x.Password) </div> }
public class AddOrEditUserViewModel { ... [Required(ErrorMessage = "رمز عبور را وارد کنید")] [MinLength(6, ErrorMessage = "حداقل 6 کاراکتر")] public string Password { get; set; } ... }
if (model.Id.HasValue) { ModelState["Password"].Errors.Clear(); } if (ModelState.IsValid) { ... }
reCAPTCHA چیست؟
استفاده آسان و امنیت بالا، جملهای میباشد که گوگل در سرتیتر تعریف آن جای داده که البته عنوان «من روبات نیستم» در سرویس استفاده شدهاست. reCAPTCHA یک سرویس رایگان برای وب سایتهای شما در جهت حفظ آن در برابر روباتهای مخرب است و از موتور تجزیه و تحلیل پیشرفتهی تشخیص انسان در برابر روباتها استفاده مینماید. reCAPTCHA را میتوان به صورت ماژول در بلاگ و یا فرمهای ثبت نام و ... جای داد که فقط با یک کلیک هویت سنجی انجام خواهد شد. گاها ممکن است بجای کلیک از شما سوالی پرسیده شود که در این صورت میبایستی تصاویر مرتبط با آن سوال را تیک زده باشید. دلیل استفاده از reCAPTCHA:
- گزارش روزانه از وضعیت موفقیت آمیز بودن هویت سنجی
- سهولت استفاده برای کاربران
- سهولت استفاده جهت برنامه نویسان
- دسترسی پذیری مناسب بدلیل وجود سؤالات تصویری و تلفظ و پخش عبارت بصورت صوتی
- امنیت بالا
آیا میتوان قالب reCAPTCHA را تغییر داد؟
جواب این سوال بله میباشد. این سرویس در دو قالب سفید و مشکی ارائه شدهاست که به صورت پیش فرض قالب سفید آن انتخاب میشود. در تصویر زیر قالبهای این سرویس را مشاهده خواهید کرد.
زبانهای پشتیبانی شده در این سرویس:
اضافه نمودن reCAPTCHA به سایت:
اگر قبلا در گوگل ثبت نام نمودهاید کافیست وارد این سایت شوید و بر روی Get reCAPTCHA کلیک نمائید؛ در غیر اینصورت میبایستی یک حساب کاربری ایجاد نماید. بعد از ورود، به کنترل پنل هدایت خواهید شد. در نمای اول به تصویر زیر برخورد خواهید کرد که از شما ثبت سایت جدید را خواستار است:
نام، دامنه سایت و مالک را وارد و ثبت نام نماید.
پس از آنکه بر روی دکمهی ثبت نام کلیک نمودید، برای شما دو کلید جدید را ثبت مینماید که منحصر به سایت شماست. Site Key رشته ای را داراست که در کدهای HTML قرار خواهد گرفت و کلید بعدی Secret Key میباشد. ارتباط سایت شما با گوگل میبایستی به صورت محرمانه محفوظ بماند.
گامهای لازم جهت نمایش سرویس در سایت:
- دستورات سمت کاربر
- دستورات سمت سرور
دستورات سمت کاربر:
کد زیر را در قبل از بسته شدن تک <head/> قرار دهید:
<script src='https://www.google.com/recaptcha/api.js'></script>
<div data-sitekey="6LdHGgwTAAAAAClKFhGthRrjBXh5AUGd4eWNCQq7"></div>
نکته: مقدار data-sitekey برابر است با رشته Site Key که گوگل برای شما ثبت نمود.
دستورات سمت سرور:
وقتی کاربر فرم حاوی کپچا را که به صورت صحیح هویت سنجی آن انجام شده باشد به سمت سرور ارسال کند، به عنوان بخشی از دادهی ارسال شده، یک رشته با نام g-recaptcha-response با دستور Request دریافت خواهید کرد که به منظور بررسی اینکه آیا گوگل تایید کرده است که کاربر، یک درخواست POST ارسال نموداست. با این پارامترها یک مقدار json برگشت داده خواهد شد که میبایستی کلاسی متناظر با آن جهت خواندن ساخته شود.
با استفاده از کد زیر مقدار برگشتی Json را در کلاس مپ مینمائیم:using System.Collections.Generic; using Newtonsoft.Json; namespace BaseConfig.Security.Captcha { public class RepaptchaResponse { [JsonProperty("success")] public bool Success { get; set; } [JsonProperty("error-codes")] public List<string> ErrorCodes { get; set; } } }
با استفاده از کلاس زیر درخواستی به گوگل ارسال شده و در صورتیکه با خطا مواجه شود با استفاده از دستور switch به آن دسترسی خواهیم یافت.
using System.Configuration; using System.Net; using Newtonsoft.Json; namespace BaseConfig.Security.Captcha { public class ReCaptcha { public static string _secret; static ReCaptcha() { _secret = ConfigurationManager.AppSettings["ReCaptchaGoogleSecretKey"]; } public static bool IsValid(string response) { //secret that was generated in key value pair var client = new WebClient(); var reply = client.DownloadString($"https://www.google.com/recaptcha/api/siteverify?secret={_secret}&response={response}"); var captchaResponse = JsonConvert.DeserializeObject<RepaptchaResponse>(reply); // when response is false check for the error message if (!captchaResponse.Success) { //if (captchaResponse.ErrorCodes.Count <= 0) return View(); //var error = captchaResponse.ErrorCodes[0].ToLower(); //switch (error) //{ // case ("missing-input-secret"): // ViewBag.Message = "The secret parameter is missing."; // break; // case ("invalid-input-secret"): // ViewBag.Message = "The secret parameter is invalid or malformed."; // break; // case ("missing-input-response"): // ViewBag.Message = "The response parameter is missing."; // break; // case ("invalid-input-response"): // ViewBag.Message = "The response parameter is invalid or malformed."; // break; // default: // ViewBag.Message = "Error occured. Please try again"; // break; //} return false; } // Captcha is valid return true; } } }
تابع IsValid از نوع برگشتی Boolean بوده و خطایی برگشت داده نخواهد شد و از این جهت به صورت کامنت برای شما گذاشته شده که میتوان متناظر با کد نویسی آن را تغییر دهید.
در اکشن زیر مقدار response برسی میشود تا خالی نباشد و همچنین مقدار آن را میتوان با استفاده از تابع IsValid در کلاس ReCaptcha به سمت گوگل فرستاد.
// // POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public virtual async Task<ActionResult> Login(LoginPageModel model, string returnUrl) { var response = Request["g-recaptcha-response"]; if (response != null && ReCaptcha.IsValid(response)) { // } }
گاها اتفاق میافتد که از دستورات Ajax برای ارسال اطلاعات به سمت سرور استفاده میشود که در این صورت لازم است بعد از پایان عملیات، کپچا ریفرش گردد. برای این کار میتوان از دستور جاوا اسکریپتی زیر استفاده نمود. در صورتیکه صفحه Postback شود، کپچا مجددا ریفرش خواهد شد.
/** * * @param {} data * @returns {} */ function Success(data) { grecaptcha.reset(); }
تا اینجا موفق شدیم تا فرم ارسالی همراه کپچا را به سمت سرور ارسال کنیم. اما ممکن است در یک صفحه از چند کپچا استفاده شود که در این صورت میبایستی دستورات سمت کاربر تغییر نمایند.
برای این کار دستور
<div data-sitekey="6LdHGgwTAAAAAClKFhGthRrjBXh5AUGd4eWNCQq7"></div>
<script> var recaptcha1; var recaptcha2; var myCallBack = function () { //Render the recaptcha1 on the element with ID "recaptcha1" recaptcha1 = grecaptcha.render('recaptcha1', { 'sitekey': '6Lf9FQwTAAAAAE6XlDqrey24K4xJOPM5nNVBmNO9', 'theme': 'light' }); //Render the recaptcha2 on the element with ID "recaptcha2" recaptcha2 = grecaptcha.render('recaptcha2', { 'sitekey': '6Lf9FQwTAAAAAE6XlDqrey24K4xJOPM5nNVBmNO9', 'theme': 'light' }); //Render the recaptcha3 on the element with ID "recaptcha3" recaptcha2 = grecaptcha.render('recaptcha3', { 'sitekey': '6Lf9FQwTAAAAAE6XlDqrey24K4xJOPM5nNVBmNO9', 'theme': 'light' }); }; </script>
برای نمایش کپچا، تگهای div با id متناظر با recaptcha1, recaptcha2, recaptcha3 ( در این مثال از سه کپچا در صفحه استفاده شده است ) در صفحه قرار خواهند گرفت.
<div id="recaptcha1"></div> <div id="recaptcha2"></div> <div id="recaptcha3"></div>
کار ما تمام شد. حال اگر پروژه را اجرا نمائید، در صفحه سه کپچا مشاهده خواهید کرد.
چند زبانه کردن کپچا:
برای چند زبانه کردن کافیست با مراجعه به این لینک و یا استفاده از تصویر بالا ( زبانهای پشتیبانی ) مقدار آن زبان را برابر با پراپرتی hl که به صورت کوئری استرینگ برای گوگل ارسال میگردد، استفاده نمود. کد زیر نمونهی استفاده شده برای زبانهای انگلیسی و فارسی میباشد که با ریسورس مقدار دهی میشود.<script src='https://www.google.com/recaptcha/api.js?hl=@(App_GlobalResources.CP.CurrentAbbrivation)'></script>
در صورتی که از فایل ریسوس استفاده نمیکنید میتوان به صورت مستقیم مقدار دهی نمائید:
<script src='https://www.google.com/recaptcha/api.js?hl=fa'></script>
برای دوستانی که با تکنولوژی ASP.Net کار میکنند، این روال هم برای آنها هم صادق میباشد.
برای دریافت پروژه اینجا کلیک نمائید.