select * from Inbox where SendDate>'1395-12-30' and SendDate<'1396-12-31'
public virtual User User { get; set; } public virtual Role Role { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; } = new HashSet<UserRole>();
آشنایی با NHibernate - قسمت سوم
من در NHibernate 2 از این کلاس استفاده می کردم:( نگاشت، مربوط به جدول tblAhkam است)
public class Ahkam
{
public virtual int Id { get; set; }
public virtual int HDate { get; set; }
public virtual string SepratedDate
{
get
{
return Functions.SepratePersianDate(HDate);//Convert 890221 to 89/02/21
}
}
}
و در لایه BLL تبدیل به Dataset می کردم و استفاده می شد و مشکلی هم وجود نداشت. اما وقتی با NHibernate 3 برنامه رو اجرا کردم به مشکل برخورد و فهمیدم چون فیلد SepratedDate در جدول بانک وجود ندارد باعث خطا شده است. راه حلی وجود دارد؟
میانافزار چندسکویی فشرده سازی صفحات در ASP.NET Core
پیشتر مطلب «استفاده از GZip توکار IISهای جدید و تنظیمات مرتبط با آنها» را در سایت جاری مطالعه کردهاید. این قابلیت صرفا وابستهاست به IIS و همچنین در صورت نصب بودن ماژول httpCompression آن کار میکند. بنابراین قابلیت انتقال به سایر سیستم عاملها را نخواهد داشت و هرچند تنظیمات فایل web.config آن هنوز هم در برنامههای ASP.NET Core معتبر هستند، اما چندسکویی نیستند. برای رفع این مشکل، تیم ASP.NET Core، میانافزار توکاری را برای فشرده سازی صفحات ارائه دادهاست که جزئی از تازههای ASP.NET Core 1.1 نیز بهشمار میرود.
برای نصب آن دستور ذیل را در کنسول پاورشل نیوگت، اجرا کنید:
PM> Install-Package Microsoft.AspNetCore.ResponseCompression
{ "dependencies": { "Microsoft.AspNetCore.ResponseCompression": "1.0.0" } }
مرحلهی بعد، افزودن سرویسهای و میان افزار مرتبط، به کلاس آغازین برنامه هستند. همیشه متدهای Add کار ثبت سرویسهای میانافزار را انجام میدهند و متدهای Use کار افزودن خود میانافزار را به مجموعهی موجود تکمیل میکنند.
public void ConfigureServices(IServiceCollection services) { services.AddResponseCompression(options => { options.MimeTypes = Microsoft.AspNetCore.ResponseCompression.ResponseCompressionDefaults.MimeTypes; }); }
namespace Microsoft.AspNetCore.ResponseCompression { /// <summary> /// Defaults for the ResponseCompressionMiddleware /// </summary> public class ResponseCompressionDefaults { /// <summary> /// Default MIME types to compress responses for. /// </summary> // This list is not intended to be exhaustive, it's a baseline for the 90% case. public static readonly IEnumerable<string> MimeTypes = new[] { // General "text/plain", // Static files "text/css", "application/javascript", // MVC "text/html", "application/xml", "text/xml", "application/json", "text/json", }; } }
services.AddResponseCompression(options => { options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "image/svg+xml", "application/font-woff2" }); });
به علاوه options ذکر شدهی در اینجا دارای خاصیت options.Providers نیز میباشد که نوع و الگوریتم فشرده سازی را مشخص میکند. در صورتیکه مقدار دهی نشود، مقدار پیش فرض آن Gzip خواهد بود:
services.AddResponseCompression(options => { //If no compression providers are specified then GZip is used by default. //options.Providers.Add<GzipCompressionProvider>();
همچنین اگر علاقمند بودید تا میزان فشرده سازی تامین کنندهی Gzip را تغییر دهید، نحوهی تنظیمات آن به صورت ذیل است:
services.Configure<GzipCompressionProviderOptions>(options => { options.Level = System.IO.Compression.CompressionLevel.Optimal; });
به صورت پیشفرض، فشرده سازی صفحات Https انجام نمیشود. برای فعال سازی آن تنظیم ذیل را نیز باید قید کرد:
options.EnableForHttps = true;
مرحلهی آخر این تنظیمات، افزودن میان افزار فشرده سازی خروجی به لیست میان افزارهای موجود است:
public void Configure(IApplicationBuilder app) { app.UseResponseCompression() // Adds the response compression to the request pipeline .UseStaticFiles(); // Adds the static middleware to the request pipeline }
تنظیمات کش کردن چندسکویی فایلهای ایستا در ASP.NET Core
تنظیمات کش کردن فایلهای ایستا در web.config مخصوص IIS به صورت ذیل است :
<staticContent> <clientCache httpExpires="Sun, 29 Mar 2020 00:00:00 GMT" cacheControlMode="UseExpires" /> </staticContent>
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseResponseCompression() .UseStaticFiles( new StaticFileOptions { OnPrepareResponse = _ => _.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=604800" // A week in seconds }) .UseMvc(routes => routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}")); }
معادل چندسکویی ماژول URL Rewrite در ASP.NET Core
مثالهایی از ماژول URL Rewrite را در مباحث بهینه سازی سایت برای بهبود SEO پیشتر بررسی کردهایم (^ و ^ و ^). این ماژول نیز همچنان در ASP.NET Core هاست شدهی در ویندوز و IIS قابل استفاده است (البته به شرطی که ماژول مخصوص آن در IIS نصب و فعال شده باشد). معادل چندسکویی این ماژول به صورت یک میانافزار توکار به ASP.NET Core 1.1 اضافه شدهاست.
برای استفادهی از آن، ابتدا نیاز است بستهی نیوگت آنرا به نحو ذیل نصب کرد:
PM> Install-Package Microsoft.AspNetCore.Rewrite
{ "dependencies": { "Microsoft.AspNetCore.Rewrite": "1.0.0" } }
پس از نصب آن، نمونهای از نحوهی تعریف و استفادهی آن در کلاس آغازین برنامه به صورت ذیل خواهد بود:
public void Configure(IApplicationBuilder app) { app.UseRewriter(new RewriteOptions() .AddRedirectToHttps() .AddRewrite(@"app/(\d+)", "app?id=$1", skipRemainingRules: false) // Rewrite based on a Regular expression //.AddRedirectToHttps(302, 5001) // Redirect to a different port and use HTTPS .AddRedirect("(.*)/$", "$1") // remove trailing slash, Redirect using a regular expression .AddRedirect(@"^section1/(.*)", "new/$1", (int)HttpStatusCode.Redirect) .AddRedirect(@"^section2/(\\d+)/(.*)", "new/$1/$2", (int)HttpStatusCode.MovedPermanently) .AddRewrite("^feed$", "/?format=rss", skipRemainingRules: false));
در اینجا مثالهایی را از اجبار به استفادهی از HTTPS، تا حذف / از انتهای مسیرهای وب سایت و یا هدایت آدرس قدیمی فید سایت، به آدرسی جدید واقع در مسیر format=rss، توسط عبارات باقاعده مشاهده میکنید.
در این تنظیمات اگر پارامتر skipRemainingRules به true تنظیم شود، به محض برآورده شدن شرط انطباق مسیر (پارامتر اول ذکر شده)، بازنویسی مسیر بر اساس پارامتر دوم، صورت گرفته و دیگر شرطهای ذکر شده، پردازش نخواهند شد.
این میانافزار قابلیت دریافت تعاریف خود را از فایلهای web.config و یا htaccess (لینوکسی) نیز دارد:
app.UseRewriter(new RewriteOptions() .AddIISUrlRewrite(env.ContentRootFileProvider, "web.config") .AddApacheModRewrite(env.ContentRootFileProvider, ".htaccess"));
و یا اگر خواستید منطق پیچیدهتری را نسبت به عبارات باقاعده اعمال کنید، میتوان یک IRule سفارشی را نیز به نحو ذیل تدارک دید:
public class RedirectWwwRule : Microsoft.AspNetCore.Rewrite.IRule { public int StatusCode { get; } = (int)HttpStatusCode.MovedPermanently; public bool ExcludeLocalhost { get; set; } = true; public void ApplyRule(RewriteContext context) { var request = context.HttpContext.Request; var host = request.Host; if (host.Host.StartsWith("www", StringComparison.OrdinalIgnoreCase)) { context.Result = RuleResult.ContinueRules; return; } if (ExcludeLocalhost && string.Equals(host.Host, "localhost", StringComparison.OrdinalIgnoreCase)) { context.Result = RuleResult.ContinueRules; return; } string newPath = request.Scheme + "://www." + host.Value + request.PathBase + request.Path + request.QueryString; var response = context.HttpContext.Response; response.StatusCode = StatusCode; response.Headers[HeaderNames.Location] = newPath; context.Result = RuleResult.EndResponse; // Do not continue processing the request } }
و سپس میتوان آنرا به عنوان یک گزینهی جدید Rewriter معرفی نمود:
app.UseRewriter(new RewriteOptions().Add(new RedirectWwwRule()));
یک نکته: در اینجا در صورت نیاز میتوان از تزریق وابستگیهای در سازندهی کلاس Rule جدید تعریف شده نیز استفاده کرد. برای اینکار باید RedirectWwwRule را به لیست سرویسهای متد ConfigureServices معرفی کرد و سپس نحوهی دریافت وهلهای از آن جهت معرفی به میانافزار بازنویسی مسیرهای وب به صورت ذیل درخواهد آمد:
var options = new RewriteOptions().Add(app.ApplicationServices.GetService<RedirectWwwRule>());
همانطور که در قسمت قبل گفته شد، در این قسمت با روش کار jQuery Mobile و pluginهای مربوط به Cordova آشنا خواهیم شد.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1">
روال کار jQuery Mobile
از آنجایی که مستندات jQuery Mobile به قدر کافی کامل هست، نیازی نیست تا در مورد تک تک آنها مثال بزنیم و از اصل مطلب دور شویم. در هر مثالی که زده خواهد شد، در صورت استفاده از ویجتی خاص، با آن آشنا خواهیم شد.
لیست کامل اتریبیوتهای -data به همراه مقادیری که میپذیرند
شما میتوانید از امکانات Theme Roller برای شخصی سازی تمهای مورد نیاز استفاده کنید.
Cordova Plugins
از این قسمت http://plugins.cordova.io/#/viewAll و این قسمت http://plugreg.com/plugins میتوانید سراغ پلاگینهای مورد نیاز خود بگردید. برای مثال وارد بخش کانفیگ پروژه شده و از قسمت plugins و تب Core یکسری از پلاگینهایی را که در Cordova گنجانده شده است، مشاهده میکنید. با کلیک بر روی دکمهی Add میتوانید آن را دانلود کرده و از APIهای آن استفاده کنید.
برای مثال پلاگین Notification را به پروژه اضافه میکنم. سپس یک فایل js را با نام custom.js به فولدر scripts در ریشه پروژه اضافه کرده و محتوای فایلهای index.html , custome.js را به شکل زیر در نظر میگیرم:
$(function() { $("#alert").on('tap', function(event) { navigator.notification.alert("اطلاعات ذخیره شد",null, "alert", "تایید"); }); $("#prompt").on('tap', function(event) { navigator.notification.prompt("برای تائید نام خود را وارد کنید", onPrompt, "prompt", "تایید", "لغو"],"نام خود"]); }); function onPrompt(results) { navigator.notification.alert(results.buttonIndex + "\n" + results.input1, null); } $("#confirm").on('tap', function(event) { navigator.notification.confirm("حذف انجام شود؟", onConfirm, "confirm", ["بله", "خیر", "نمیدانم"]); }); function onConfirm(buttonIndex) { navigator.notification.alert(buttonIndex , null); } $("#beep").on('tap', function(event) { navigator.notification.beep(1); }); });
رخداد tap زمانی صادر میشود که کاربر، دکمهی مورد نظر را لمس کند و یکی از رخدادهای jQuery Mobile میباشد. بعد از نصب پلاگین Notification، با استفاده از navigator.notification میتوانید به متدهای مورد نظر که در بالا مشخص است، دسترسی پیدا کنید.
برای آشنایی با این پلاگین میتوانید داکیومنت آن را مطالعه کنید.
در کد بالا با استفاده از متدهای callback توانستهایم اطلاعاتی در مورد نوع عملکرد کاربر با notification ما بدست آوریم.
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>CordovaApp01</title> <meta name="viewport" content="width=device-width, initial-scale=1"/> <!-- CordovaApp01 references --> <link href="css/index.css" rel="stylesheet" /> <link href="jquery.mobile.rtl/css/themes/default/rtl.jquery.mobile-1.4.0.css" rel="stylesheet" /> </head> <body> <div data-role="page" id="page1"> <div data-role="header"> <h2> تست پلاگین Notification </h2> </div> <div data-role="content"> <a href="#page2" data-transition="pop" data-rel="dialog" data-role="button" data-inline="true" data-icon="back">page 2</a> <button data-role="button" id="alert" data-inline="true" >alert</button> <button data-role="button" id="confirm" data-inline="true">confirm</button> <button data-role="button" id="beep" data-inline="true" >beep</button> <button data-role="button" id="prompt" data-inline="true" >prompt</button> </div> <div data-role="footer"> <h2>من فوتر هستم</h2> </div> </div> <div data-role="page" id="page2"> <div data-role="header"> <h1>Header</h1> </div> <div data-role="content"> Content </div> <div data-role="footer"> <h1>Footer</h1> </div> </div> <!-- Cordova reference, this is added to your app when it's built. --> <script src="scripts/jquery-2.1.3.min.js"></script> <script src="cordova.js"></script> <script src="scripts/platformOverrides.js"></script> <script src="scripts/index.js"></script> <script src="jquery.mobile.rtl/js/rtl.jquery.mobile-1.4.0.js"></script> <script src="scripts/custom.js"></script> </body> </html>
در کد بالا 4 تا button دیده میشود که ویژگی data-role آنها مقدار button در نظر گرفته شدهاست تا توسط jQuery Mobile به عنوان button شناخته شوند و استایلهای لازم بر روی آنها اعمال گردد. قرار است طبق کد js ایی که نوشتهایم، با لمس کردن هر کدام از دکمهها، notification هایی نمایش داده شوند.
برای اینکار شبیه ساز YouWave را دانلود کرده و نصب کنید. سپس در قسمت toolbar ویژوال، گزینهی Device را به جای شبیه ساز Ripple انتخاب کنید. نرم افزار youwave را اجرا کنید حال اگر برنامه را اجرا کنید با خطای زیر مواجه خواهید شد:
Error447C:\Users\Administrator\Documents\Visual Studio 2013\Projects\CordovaApp-01\CordovaApp-01\bld\Debug\platforms\android\cordova\node_modules\q\q.js:126CordovaApp-01 Error448throw e;CordovaApp-01 Error449^CordovaApp-01 Error450Error : DEP10201 : Failed to deploy to device, no devices found.CordovaApp-01
adb connect localhost:5558
<a href="#page2" data-transition="pop" data-rel="dialog" data-role="button" data-inline="true" data-icon="back">page 2</a>
در مقالهی بعد، به مباحث Database در Cordova خواهیم پرداخت.
ادامه دارد...
تغییر نوع DbContext برنامه
پیش از شروع به یکپارچه کردن ASP.NET Core Identity با برنامهی جاری، نیاز است نوع DbContext آنرا به صورت زیر تغییر داد:
using BlazorServer.Entities; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; namespace BlazorServer.DataAccess { public class ApplicationDbContext : IdentityDbContext { // ...
- این تغییر، نیاز به نصب بستهی نیوگت Microsoft.AspNetCore.Identity.EntityFrameworkCore را نیز در پروژهی جاری دارد تا IdentityDbContext آن شناسایی شده و قابل استفاده شود.
نصب ابزار تولید کدهای ASP.NET Core Identity
اگر از ویژوال استودیوی کامل استفاده میکنید، گزینهی افزودن کدهای ASP.NET Core Identity به صورت زیر قابل دسترسی است:
project -> right-click > Add > New Scaffolded Item -> select Identity > Add
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design dotnet add package Microsoft.EntityFrameworkCore.Design dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore dotnet add package Microsoft.AspNetCore.Identity.UI dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet aspnet-codegenerator identity --dbContext BlazorServer.DataAccess.ApplicationDbContext --force
حال اگر به پروژه دقت کنیم، پوشهی جدید Areas که به همراه فایلهای مدیریتی ASP.NET Core Identity است، اضافه شده و حاوی کدهای صفحات لاگین، ثبت نام کاربر و غیره است.
اعمال تغییرات ابتدایی مورد نیاز جهت استفاده از ASP.NET Core Identity
تا اینجا کدهای پیشفرض مدیریتی ASP.NET Core Identity را به پروژه اضافه کردیم. در ادامه نیاز است تغییرات ذیل را به پروژهی اصلی Blazor Server اعمال کنیم تا بتوان از این فایلها استفاده کرد:
- به فایل BlazorServer.App\Startup.cs مراجعه کرده و UseAuthentication و UseAuthorization را دقیقا در محلی که مشاهده میکنید، اضافه میکنیم. همچنین در اینجا نیاز است مسیریابیهای razor pages را نیز فعال کرد.
namespace BlazorServer.App { public class Startup { // ... public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // ... app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); // ... }); } } }
dotnet tool update --global dotnet-ef --version 5.0.4 dotnet build dotnet ef migrations --startup-project ../BlazorServer.App/ add AddIdentity --context ApplicationDbContext dotnet ef --startup-project ../BlazorServer.App/ database update --context ApplicationDbContext
افزودن گزینهی منوی لاگین به برنامهی Blazor Server
پس از این تغییرات، به برنامهای رسیدهایم که مدیریت قسمت Identity آن، توسط قالب استاندارد مایکروسافت که در پوشهی Areas\Identity\Pages\Account نصب شده و بر اساس فناوری ASP.NET Core Razor Pages کار میکند، انجام میشود.
اکنون میخواهیم در منوی برنامهی Blazor Server خود که با صفحات Identity یکی شدهاست، لینکی را به صفحهی لاگین این Area اضافه کنیم. اگر به فایل Shared\MainLayout.razor آن مراجعه کنیم، به صورت پیشفرض، لینکی به صفحهی About، قرار دارد. به همین جهت این مورد را به صورت زیر اصلاح میکنیم:
ابتدا کامپوننت جدید BlazorServer.App\Shared\LoginDisplay.razor را با محتوای زیر ایجاد میکنیم:
<a href="Identity/Account/Register">Register</a> <a href="Identity/Account/Login">Login</a> @code { }
سپس از این کامپوننت در فایل BlazorServer.App\Shared\MainLayout.razor استفاده میکنیم:
<div class="top-row px-4"> <LoginDisplay></LoginDisplay> <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a> </div>
ثبت و فعالسازی سرویسهای کار با ASP.NET Core Identity
البته اگر در این حال برنامه را اجرا کنیم، با کلیک بر روی لینکهای فوق، استثنائی را مانند یافت نشدن سرویس UserManager، مشاهده خواهیم کرد. برای رفع این مشکل، به فایل BlazorServer.App\Startup.cs مراجعه کرده و سرویسهای Identity را ثبت میکنیم:
namespace BlazorServer.App { public class Startup { // ... public void ConfigureServices(IServiceCollection services) { // ... services.AddIdentity<IdentityUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders() .AddDefaultUI(); // ...
همانطور که مشاهده میکنید، قالب این قسمت Identity، با قالب قسمت Blazor Server یکی نیست؛ چون توسط Razor Pages و Area آن تامین میشود که master page خاص خودش را دارد. زمانیکه قالب Identity را اضافه میکنیم، علاوه بر Area خاص خودش، پوشهی جدید Pages\Shared را نیز ایجاد میکند که قالب صفحات Identity را به کمک فایل Pages\Shared\_Layout.cshtml تامین میکند:
بنابراین سفارشی سازی قالب این قسمت، شبیه به قالبی که برای کامپوننتهای Blazor مورد استفاده قرار میگیرد، باید در اینجا انجام شود و سفارشی سازی قالب کامپوننتهای Blazor، در پوشهی Shared ای که در ریشهی پروژهاست (BlazorServer.App\Shared\MainLayout.razor) انجام میشود.
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-21.zip
- به همین جهت تنها کاری که باید انجام داد، افزودن ارجاعی به کلاسهای مدل مورد نظر هست. پس از آن شبیه به کاری که در DatabaseSeeder انجام شده، میتوان با استفاده از متد Set، به کلیه امکانات مدلی خاص دسترسی یافت:
DatabaseSeeder = uow => { var news = uow.Set<News>();
ولی در کل افزودن ارجاعی به کلاسهای مدل دیگر، مشکل ساز نیست؛ چون این کلاسها عملا منطق خاصی را پیاده سازی نمیکنند و همچنین وابستگی خاصی هم به پروژهی خاصی ندارند. یک سری کلاس دارای خاصیتهای get/set دار معمولی هستند به همراه تنظیمات آنها.
برای مثال در برنامههای ASP.NET Core، یک چنین فرمی:
<form asp-controller="Manage" asp-action="ChangePassword" method="post"> <!-- Form details --> </form>
<form method="post" action="/Manage/ChangePassword"> <!-- Form details --> <input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkSldwD9CpLR...LongValueHere!" /> </form>
تولید خودکار کوکیهای Anti-forgery tokens برای برنامههای Angular
در سمت Angular، مطابق مستندات رسمی آن (^ و ^)، اگر کوکی تولید شدهی توسط برنامه، دارای نام مشخص «XSRF-TOKEN» باشد، کتابخانهی HTTP آن به صورت خودکار مقدار آنرا استخراج کرده و به درخواست بعدی ارسالی آن اضافه میکند. بنابراین در سمت ASP.NET Core تنها کافی است کوکی مخصوص فوق را تولید کرده و به Response اضافه کنیم. مابقی آن توسط Angular به صورت خودکار مدیریت میشود.
میتوان اینکار را مستقیما داخل متد Configure کلاس آغازین برنامه انجام داد و یا بهتر است جهت حجیم نشدن این فایل و مدیریت مجزای این مسئولیت، یک میانافزار مخصوص آنرا تهیه کرد:
using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; namespace AngularTemplateDrivenFormsLab.Utils { public class AntiforgeryTokenMiddleware { private readonly RequestDelegate _next; private readonly IAntiforgery _antiforgery; public AntiforgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery) { _next = next; _antiforgery = antiforgery; } public Task Invoke(HttpContext context) { var path = context.Request.Path.Value; if (path != null && !path.StartsWith("/api/", StringComparison.OrdinalIgnoreCase)) { var tokens = _antiforgery.GetAndStoreTokens(context); context.Response.Cookies.Append( key: "XSRF-TOKEN", value: tokens.RequestToken, options: new CookieOptions { HttpOnly = false // Now JavaScript is able to read the cookie }); } return _next(context); } } public static class AntiforgeryTokenMiddlewareExtensions { public static IApplicationBuilder UseAntiforgeryToken(this IApplicationBuilder builder) { return builder.UseMiddleware<AntiforgeryTokenMiddleware>(); } } }
- در اینجا ابتدا سرویس IAntiforgery به سازندهی کلاس میان افزار تزریق شدهاست. به این ترتیب میتوان به سرویس توکار تولید توکنهای Antiforgery دسترسی یافت. سپس از این سرویس جهت دسترسی به متد GetAndStoreTokens آن برای دریافت محتوای رشتهای نهایی این توکن استفاده میشود.
- اکنون که به این توکن دسترسی پیدا کردهایم، تنها کافی است آنرا با کلید مخصوص XSRF-TOKEN که توسط Angular شناسایی میشود، به مجموعهی کوکیهای Response اضافه کنیم.
- علت تنظیم مقدار خاصیت HttpOnly به false، این است که کدهای جاوا اسکریپتی Angular بتوانند به مقدار این کوکی دسترسی پیدا کنند.
پس از تدارک این مقدمات، کافی است متد الحاقی کمکی UseAntiforgeryToken فوق را به نحو ذیل به متد Configure کلاس آغازین برنامه اضافه کنیم؛ تا کار نصب میان افزار AntiforgeryTokenMiddleware، تکمیل شود:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseAntiforgeryToken();
پردازش خودکار درخواستهای ارسالی از طرف Angular
تا اینجا برنامهی سمت سرور ما کوکیهای مخصوص Angular را با کلیدی که توسط آن شناسایی میشود، تولید کردهاست. در پاسخ، Angular این کوکی را در هدر مخصوصی به نام «X-XSRF-TOKEN» به سمت سرور ارسال میکند (ابتدای آن یک X اضافهتر دارد).
به همین جهت به متد ConfigureServices کلاس آغازین برنامه مراجعه کرده و این هدر مخصوص را معرفی میکنیم تا دقیقا مشخص گردد، این توکن از چه قسمتی باید جهت پردازش استخراج شود:
public void ConfigureServices(IServiceCollection services) { services.AddAntiforgery(x => x.HeaderName = "X-XSRF-TOKEN"); services.AddMvc(); }
یک نکته: اگر میخواهید این کلیدهای هدر پیش فرض Angular را تغییر دهید، باید یک CookieXSRFStrategy سفارشی را برای آن تهیه کنید.
اعتبارسنجی خودکار Anti-forgery tokens در برنامههای ASP.NET Core
ارسال کوکی اطلاعات Anti-forgery tokens و سپس دریافت آن توسط برنامه، تنها یک قسمت از کار است. قسمت بعدی، بررسی معتبر بودن آنها در سمت سرور است. روش متداول انجام اینکار، افزودن ویژگی [ValidateAntiForgeryToken] به هر اکشن متد مزین به [HttpPost] است:
[HttpPost] [ValidateAntiForgeryToken] public IActionResult ChangePassword() { // ... return Json(…); }
public void ConfigureServices(IServiceCollection services) { services.AddAntiforgery(x => x.HeaderName = "X-XSRF-TOKEN"); services.AddMvc(options => { options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); }); }
یک نکته: در این حالت بررسی سراسری، اگر در موارد خاصی نیاز به این اعتبارسنجی خودکار نبود، میتوان از ویژگی [IgnoreAntiforgeryToken] استفاده کرد.
آزمایش برنامه
برای آزمایش مواردی را که تا کنون بررسی کردیم، همان مثال «فرمهای مبتنی بر قالبها در Angular - قسمت پنجم - ارسال اطلاعات به سرور» را بر اساس نکات متدهای ConfigureServices و Configure مطلب جاری تکمیل میکنیم. سپس برنامه را اجرا میکنیم:
همانطور که ملاحظه میکنید، در اولین بار درخواست برنامه، کوکی مخصوص Angular تولید شدهاست.
در ادامه اگر فرم را تکمیل کرده و ارسال کنیم، وجود هدر ارسالی از طرف Angular مشخص است و همچنین خروجی هم با موفقیت دریافت شدهاست:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-09.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس به ریشهی پروژه وارد شده و دو پنجرهی کنسول مجزا را باز کنید. در اولی دستورات
>npm install >ng build --watch
>dotnet restore >dotnet watch run