نظرات مطالب
افزودن و اعتبارسنجی خودکار Anti-Forgery Tokens در برنامه‌های Angular مبتنی بر ASP.NET Core
سلام
من از ASP. Net Web API 2  برای سرویس هام استفاده می‌کنم، برای سمت Front از Angular 6
چطور میتونم Anti-Forgery Token رو توش پیاده سازی کنم، متاسفانه مطلب مفیدی پیدا نکردم که کمک کنه. (نمونه همین مورد رو با AngularJS پیاده کردم، ولی ظاهرا اینجا یه کم متفاوت هست)
ممنون میشم راهنمایی کنید
نظرات مطالب
یکپارچه سازی Angular CLI و ASP.NET Core در VS 2017
یک نکته‌ی تکمیلی
Microsoft.DotNet.Web.Spa.ProjectTemplates  در آخرین نگارش آن، پشتیبانی از Angular CLI را هم افزوده‌است. برای کار با آن و ایجاد یک پروژه‌ی جدید بر مبنای آن دستورات ذیل را صادر کنید:
> dotnet new --install Microsoft.DotNet.Web.Spa.ProjectTemplates::2.0.0-preview1-final
> dotnet new angular
سپس اگر به فایل‌های Startup.cs و csproj آن دقت کنید، نحوه‌ی استفاده‌ی از بسته‌ی نیوگت Microsoft.AspNetCore.SpaServices.Extensions را جهت معرفی مسیر ClientApp/dist و راه اندازی خودکار UseAngularCliServer مشاهده خواهید کرد.

توضیحات بیشتر:
About The Updated SPA Templates From ASP.NET Core 
Migrating from the old ASP.NET Core Angular Spa template to the newer one  
مطالب
Angular CLI - قسمت پنجم - ساخت و توزیع برنامه
ساخت و توزیع برنامه‌های Angular یکی از مهم‌ترین و بحث برانگیزترین قسمت‌های نگارش‌های جدید آن است و به ازای هر پروژه و قالبی که برای آن توسط گروه‌های مختلف ارائه شده‌است، روش‌های متفاوتی را شاهد خواهید بود. در ادامه روش توصیه شده‌ی توسط تیم Angular را که مبتنی است بر webpack و به صورت خودکار توسط Angular CLI مدیریت می‌شود، بررسی خواهیم کرد.


ساخت (Build) برنامه‌های Angular

Angular CLI کار ساخت و کامپایل برنامه را به صورت خودکار انجام داده و خروجی را در مسیری مشخص درج می‌کند. در اینجا می‌توان گزینه‌هایی را بر اساس نوع کامپایل مدنظر مانند کامپایل برای حالت توسعه و یا کامپایل برای حالت توزیع نهایی، انتخاب کرد. همچنین مباحث bundling و یکی کردن تعداد بالای ماژول‌های برنامه در آن لحاظ می‌شوند تا برنامه در حالت توزیع نهایی، سبب 100ها رفت و برگشت به سرور برای دریافت ماژول‌های مختلف آن نشود. به علاوه مباحث uglification (به نوعی obfuscation کدهای جاوا اسکریپتی نهایی) و tree-shaking (حذف کدهایی که در برنامه استفاده نشده‌اند؛ یا کدهای مرده) نیز پیاده سازی می‌شوند. با انجام tree-shaking‌، نه تنها اندازه‌ی توزیع نهایی به کاربر کاهش پیدا می‌کند، بلکه مرورگر نیز حجم کمتری از کدهای جاوااسکریپتی را باید تفسیر کند.
برای شروع می‌توان از دستور ذیل برای مشاهده‌ی تمام گزینه‌های مهیای ساخت برنامه استفاده کرد:
> ng build --help
ذکر تنهای دستور ng build‌، بدون هیچ گزینه‌ای، برای حالت «توسعه‌ی» برنامه بسیار ایده‌آل است (و دقیقا به معنای صدور دستور ng build --dev است). در این حالت خروجی کامپایل شده‌ی برنامه در پوشه‌ی dist تولید می‌شود. اگر از قسمت دوم این سری به خاطر داشته باشید، نام این پوشه‌ی خروجی، جزئی از تنظیمات فایل angular-cli.json. است:
"apps": [
{
   "outDir": "dist",
زمانیکه دستور ng build‌  صادر شود، این فایل‌ها را در پوشه‌ی dist خواهید یافت:

فایل 
توضیح 
 inline.bundle.js   WebPack runtime
از آن برای بارگذاری ماژول‌های برنامه و چسباندن قسمت‌های مختلف به یکدیگر استفاده می‌شود. 
 main.bundle.js   شامل تمام کدهای ما است. 
 polyfills.bundle.js   Polyfills - جهت پشتیبانی از مرورگرهای مختلف.
 styles.bundle.js    شامل بسته بندی تمام شیوه نامه‌های برنامه است 
vendor.bundle.js  کدهای کتابخانه‌های ثالث مورد استفاده و همچنین خود Angular، در اینجا بسته بندی می‌شوند. 
 

روشی برای بررسی محتوای bundleهای تولید شده

تولید bundleها در جهت کاهش رفت و برگشت‌های به سرور و بالا بردن کارآیی برنامه ضروری هستند؛ اما دقیقا این بسته بندی‌ها شامل چه اطلاعاتی می‌شوند؟ این اطلاعات را می‌توان از فایل‌های source map تولیدی استخراج کرد و برای این منظور می‌توان از برنامه‌ی source-map-explorer استفاده کرد.

روش نصب عمومی آن:
 > npm install -g source-map-explorer
روش اجرا:
 > source-map-explorer dist/main.bundle.js
پس از آن یک گزارش HTML ایی از محتوای bundle مدنظر تولید می‌شود.


یک مثال: ساخت برنامه‌ی مثال قسمت چهارم - تنظیمات مسیریابی در حالت dev

در ادامه، کار Build همان مثالی را که در قسمت قبل توضیح داده شد، بررسی می‌کنیم. برای این منظور از طریق خط فرمان به ریشه‌ی پوشه‌ی اصلی پروژه وارد شده و دستور ng build را صادر کنید. یک چنین خروجی را مشاهده خواهید کرد:
 D:\Prog\angular-routing>ng build
Hash: 123cae8bd8e571f44c31
Time: 33862ms
chunk {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 158 kB {4} [initial] [rendered]
chunk {1} main.bundle.js, main.bundle.js.map (main) 14.7 kB {3} [initial] [rendered]
chunk {2} styles.bundle.js, styles.bundle.js.map (styles) 9.77 kB {4} [initial] [rendered]
chunk {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.34 MB [initial] [rendered]
chunk {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]
و اگر فایل index.html تولیدی آن‌را بررسی کنید، تنها الحاق همین 4 فایل js تولیدی را مشاهده می‌نمائید:
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>AngularRouting</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root>Loading...</app-root>
<script type="text/javascript" src="inline.bundle.js">
</script><script type="text/javascript" src="polyfills.bundle.js">
</script><script type="text/javascript" src="styles.bundle.js">
</script><script type="text/javascript" src="vendor.bundle.js">
</script><script type="text/javascript" src="main.bundle.js"></script>
</body>
</html>

یک نکته: زمانیکه دستور ng serve -o صادر می‌شود، در پشت صحنه دقیقا همین دستور ng build صادر شده و اطلاعات را درون حافظه تشکیل می‌دهد. اما اگر کار ng build را دستی انجام دهیم، اینبار ng serve -o اطلاعات را از پوشه‌ی dist دریافت می‌کند. بنابراین در حین کار با ng serve -o نیازی به build دستی پروژه نیست.

سؤال: چرا حجم فایل endor.bundle.js اینقدر بالا است و شامل چه اجزایی می‌شود؟
نکته‌ای که در اینجا وجود دارد، حجم بالای فایل vendor.bundle.js آن است که 2.34 MB می‌باشد:


چون دستور ng build بدون پارامتری ذکر شده‌است، برنامه را برای حالت توسعه Build می‌کند و به همین جهت هیچگونه بهینه سازی در این مرحله صورت نخواهد گرفت. برای بررسی محتوای این فایل می‌توان دستور ذیل را در ریشه‌ی اصلی پروژه صادر کرد:
 > source-map-explorer dist/vendor.bundle.js
پس از اجرای این دستور، بلافاصله مرورگر پیش فرض سیستم اجرا شده و گزارشی را ارائه می‌دهد.


همانطور که مشاهده می‌کنید، در حالت بهینه سازی نشده و Build برای توسعه، کامپایلر Angular حدود 41 درصد حجم فایل vendor.bundle.js را تشکیل می‌دهد. به علاوه ماژول‌ها و قسمت‌هایی را ملاحظه می‌کنید که اساسا برنامه‌ی فعلی مثال ما از آن‌ها استفاده نمی‌کند؛ مانند http، فرم‌ها و غیره.


سفارشی سازی Build برای محیط‌های مختلف

اگر به پروژه‌ی تولید شده‌ی توسط Angular CLI دقت کنید، حاوی پوشه‌ای است به نام src\environments


هدف از فایل‌های environment برای نمونه تغییر آدرس توزیع برنامه در حالت توسعه و ارائه نهایی است.
همچنین در اینجا می‌توان نحوه‌ی بهینه سازی فایل‌های تولیدی را توسط Build Targets مشخص کرد و اینکار توسط ذکر پرچم prod-- (مخفف production) صورت می‌گیرد.
در ادامه، تفاوت‌های دستورهای ng build و ng build --prod را ملاحظه می‌کنید:
- با اجرای ng build، از فایل environment.ts استفاده می‌شود؛ برخلاف حالت اجرای ng build --prod که از فایل environment.prod.ts استفاده می‌کند.
- Cache-busting در حالت ارائه‌ی نهایی، به تمام اجزای پروژه اعمال می‌شود؛ اما در حالت توسعه فقط برای تصاویر قید شده‌ی در فایل‌های css.
- فایل‌های source map فقط برای حالت توسعه تولید می‌شوند.
- در حالت توسعه، cssها داخل فایل‌های js تولیدی قرار می‌گیرند؛ اما در حالت ارائه‌ی نهایی به صورت فایل‌های css بسته بندی می‌شوند.
- در حالت توسعه برخلاف حالت ارائه‌ی نهایی، کار uglification انجام نمی‌شود.
- در حالت توسعه برخلاف حالت ارائه‌ی نهایی، کار tree-shaking یا حذف کدهای مرده و بدون ارجاع، انجام نمی‌شود.
- در حالت توسعه برخلاف حالت ارائه‌ی نهایی، کار AOT انجام نمی‌شود. در اینجا AOT به معنای Ahead of time compilation است.
- در هر دو حالت توسعه و ارائه‌ی نهایی کار bundling و دسته بندی فایل‌ها انجام خواهد شد.

به همین جهت است که ng build سریع است؛ اما حجم بالاتری را هم تولید می‌کند. چون بسیاری از بهینه سازی‌های حالت ارائه‌ی نهایی را به همراه ندارد.


دستورات build برای حالت توسعه و ارائه‌ی نهایی

برای حالت توسعه، هر 4 دستور ذیل یک مفهوم را دارند و به همین جهت مورد ng build متداول‌تر است:
>ng build --target=development --environment=dev
>ng build --dev -e=dev
>ng build --dev
>ng build

برای حالت ارائه‌ی نهایی، هر 3 دستور ذیل یک مفهوم را دارند و به همین جهت مورد ng build --prod متداول‌تر است:
>ng build --target=production --environment=prod
>ng build --prod -e=prod
>ng build --prod

همچنین هر کدام از این دستورات را توسط پرچم‌های ذیل نیز می‌توان سفارشی سازی کرد:

 پرچم  مخفف  توضیح
 sourcemap--  sm-  تولید سورس‌مپ
aot--    Ahead of Time compilation 
watch--  w-  تحت نظر قرار دادن فایل‌ها و ساخت مجدد
environment--  e-  محیط ساخت
 target--  t-  نوع ساخت
 dev--    مخفف نوع ساخت جهت توسعه
 prod--     مخفف نوع ساخت جهت ارائه نهایی

برای مثال در حالت prod، سورس‌مپ‌ها تولید نخواهند شد. اگر علاقمندید تا این فایل‌ها نیز تولید شوند، پرچم souremap را نیز ذکر کنید.
و یا اگر برای حالت dev می‌خواهید AOT را فعالسازی کنید، پرچم aot-- را در آنجا قید کنید.


یک مثال: ساخت برنامه‌ی مثال قسمت چهارم - تنظیمات مسیریابی در حالت prod

تا اینجا خروجی حالت dev ساخت برنامه‌ی قسمت چهارم را بررسی کردیم. در ادامه دستور ng build --prod را در ریشه‌ی پروژه صادر می‌کنیم:
 D:\Prog\angular-routing>ng build --prod
Hash: f5bd7fd555a85af8a86f
Time: 39932ms
chunk {0} polyfills.18173234f9641113b9fe.bundle.js (polyfills) 158 kB {4} [initial] [rendered]
chunk {1} main.c6958def7c5f51c45261.bundle.js (main) 50.3 kB {3} [initial] [rendered]
chunk {2} styles.d41d8cd98f00b204e980.bundle.css (styles) 69 bytes {4} [initial] [rendered]
chunk {3} vendor.b426ba6883193375121e.bundle.js (vendor) 1.37 MB [initial] [rendered]
chunk {4} inline.8cec210370dd3af5f1a0.bundle.js (inline) 0 bytes [entry] [rendered]


همانطور که ملاحظه می‌کنید، اینبار نه تنها حجم فایل‌ها به میزان قابل ملاحظه‌ای کاهش پیدا کرده‌اند، بلکه این نام‌ها به همراه یک سری hash هم هستند که کار cache-busting (منقضی کردن کش مرورگر، با ارائه‌ی نگارشی جدید) را انجام می‌دهند.

در ادامه اگر بخواهیم مجددا برنامه‌ی source-map-explorer را جهت بررسی محتوای فایل‌های js اجرا کنیم، به خطای عدم وجود sourcemapها خواهیم رسید (چون در حالت prod، به صورت پیش فرض غیرفعال هستند). به همین‌جهت برای این مقصود خاص نیاز است از پرچم فعالسازی موقت آن استفاده کرد:
> ng build --prod --sourcemap
> source-map-explorer dist/vendor.b426ba6883193375121e.bundle.js


همانطور که در تصویر نیز مشخص است، اینبار کامپایلر Angular به همراه تمام ماژول‌هایی که در برنامه ارجاعی به آن‌ها وجود نداشته‌است، حذف شده‌اند و کل حجم بسته‌ی Angular به 366 KB کاهش یافته‌است.


بررسی دستور ng serve

تا اینجا برای اجرای برنامه در حالت dev از دستور ng serve -o استفاده کرده‌ایم. کار ارائه‌ی برنامه توسط این دستور، از محتوای کامپایل شده‌ی درون حافظه با مدیریت webpack انجام می‌شود. به همین جهت بسیار سریع بوده و قابلیت live reload را ارائه می‌دهد (نمایش آنی تغییرات در مرورگر، با تغییر فایل‌ها).
همانند تمام دستورات دیگر، اطلاعات بیشتری را در مورد این دستور، از طریق راهنمای آن می‌توان به دست آورد:
 > ng serve --help

که شامل این موارد هستند (علاوه بر تمام مواردی را که در حالت ng build می‌توان مشخص کرد؛ مثلا ng serve --prod -o):

 پرچم مخفف
توضیح
 open-- o-
بازکردن خودکار مرورگر پیش فرض.
حالت پیش فرض آن گشودن مرورگر توسط خودتان است و سپس مراجعه‌ی دستی به آدرس برنامه. 
 port--  p-  تغییر پورت پیش فرض مانند ng server -p 8626 
 live-reload--  lr-   فعال است مگر اینکه آن‌را با false مقدار دهی کنید.
 ssl--    ارائه به صورت HTTPS
 proxy-config--  pc-  Proxy configuration file 


استخراج فایل تنظیمات webpack از Angular CLI

Angular CLI برای مدیریت build، در پشت صحنه از webpack استفاده می‌کند. فایل تنظیمات آن نیز جزئی از فایل‌های توکار این ابزار است و قرار نیست به صورت پیش فرض و مستقیم توسط پروژه‌ی جاری ویرایش شود. به همین جهت آن‌را در ساختار پروژه‌ی تولید شده، مشاهده نمی‌کنید.
اگر علاقمند به سفارشی سازی بیشتر این تنظیمات پیش فرض باشید، ابتدا باید آن‌را اصطلاحا eject کنید و سپس می‌توان آن‌را ویرایش کرد:
 > ng eject
Ejection was successful.

To run your builds, you now need to do the following commands:
- "npm run build" to build.
- "npm run test" to run unit tests.
- "npm start" to serve the app using webpack-dev-server.
- "npm run e2e" to run protractor.

Running the equivalent CLI commands will result in an error.
============================================
Some packages were added. Please run "npm install".
همانطور که مشاهده می‌کنید عنوان کرده‌است که از این پس خودتان باید بسیاری از مسایل را به صورت دستی مدیریت کنید و Angular CLI دیگر آن‌ها را به صورت خودکار مدیریت نمی‌کند و دیگر دستورات ng build و ng serve کار نخواهند کرد (این تغییرات در فایل package.json درج می‌شوند).
در این حالت است که فایل webpack.config.js به ریشه‌ی پروژه جهت سفارشی سازی شما اضافه خواهد شد. همچنین فایل‌های .angular-cli.json، package.json نیز جهت درج این تغییرات ویرایش می‌شوند.

و اگر در این لحظه پشیمان شده‌اید (!) فقط کافی است تا این مرحله‌ی جدید commit شده‌ی به مخزن کد را لغو کنید و باز هم به همان Angular CLI قبلی می‌رسید.
مطالب
کار با SignalR Core از طریق یک کلاینت Angular
نگارش AspNetCore.SignalR 1.0.0-alpha1-final چند روزی هست که منتشر شده‌است. در این مطلب قصد داریم یک برنامه‌ی وب ASP.NET Core 2.0 را به همراه یک Hub ایجاد کرده و سپس این Hub را در یک کلاینت Angular (2+) مورد استفاده قرار دهیم.


پیشنیازها

برای دنبال کردن این مثال فرض بر این است که NET Core 2.0 SDK. و همچنین Angular CLI را نیز پیشتر نصب کرده‌اید. مابقی بحث توسط خط فرمان و ابزارهای dotnet cli و angular cli ادامه داده خواهند شد و الزامی به نصب هیچگونه IDE نیست و این مثال تنها توسط VSCode پیگیری شده‌است.


تدارک ساختار ابتدایی مثال جاری


ساخت برنامه‌ی وب، توسط dotnet cli
ابتدا یک پوشه‌ی جدید را به نام SignalRCore2Sample ایجاد می‌کنیم. سپس داخل این پوشه، پوشه‌ی دیگری را به نام SignalRCore2WebApp ایجاد خواهیم کرد (تصویر فوق). از طریق خط فرمان به این پوشه وارد شده (در ویندوز، در نوار آدرس، دستور cmd.exe را تایپ و enter کنید) و سپس فرمان ذیل را صادر می‌کنیم:
 dotnet new mvc
این دستور، یک برنامه‌ی جدید ASP.NET Core 2.0 را تولید خواهد کرد.

ساخت برنامه‌ی کلاینت، توسط angular cli
سپس از طریق خط فرمان به پوشه‌ی SignalRCore2Sample بازگشته و دستور ذیل را صادر می‌کنیم:
 ng new SignalRCore2Client
این دستور، یک برنامه‌ی Angular را در پوشه‌ی SignalRCore2Client تولید می‌کند (تصویر فوق).

اکنون که در پوشه‌ی ریشه‌ی SignalRCore2Sample قرار داریم، اگر در خط فرمان، دستور . code را صادر کنیم، VSCode هر دو پوشه‌ی وب و client را با هم در اختیار ما قرار می‌دهد:


تکمیل پیشنیازهای برنامه‌ی وب

پس از ایجاد ساختار اولیه‌ی برنامه‌های وب ASP.NET Core و کلاینت Angular، اکنون نیاز است وابستگی جدید AspNetCore.SignalR را به آن معرفی کنیم. به همین جهت به فایل SignalRCore2WebApp.csproj مراجعه کرده و تغییرات ذیل را به آن اعمال می‌کنیم:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0-alpha1-final" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
    <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" />
  </ItemGroup>
</Project>
در اینجا ابتدا بسته‌ی Microsoft.AspNetCore.SignalR اضافه شده‌است. همچنین Microsoft.DotNet.Watcher.Tools را نیز اضافه کرده‌ایم تا بتوان از مزیت build تدریجی پروژه، به ازای هر تغییر صورت گرفته، استفاده کنیم.
پس از این تغییرات، دستور ذیل را در خط فرمان صادر می‌کنیم تا وابستگی‌های پروژه نصب شوند:
 dotnet restore
البته اگر افزونه‌ی #C مخصوص VSCode را نصب کرده باشید، تغییرات فایل csproj را دنبال کرده و پیام restore را نیز ظاهر می‌کند؛ تا همین دستور فوق را به صورت خودکار اجرا کند.
یک نکته: نگارش فعلی افزونه‌ی #C مخصوص VSCode، با تغییر فایل csproj و restore وابستگی‌های آن نیاز دارد یکبار آن‌را بسته و سپس مجددا اجرا کنید، تا اطلاعات intellisense خود را به روز رسانی کند. بنابراین اگر VSCode بلافاصله کلاس‌های مرتبط با بسته‌های جدید را تشخیص نمی‌دهد، علت صرفا این موضوع است.

پس از بازیابی وابستگی‌ها، به ریشه‌ی پروژه‌ی برنامه‌ی وب وارد شده و دستور ذیل را صادر کنید:
 dotnet watch run
این دستور، پروژه را build کرده و سپس بر روی پورت 5000 ارائه می‌دهد. همچنین به ازای هر تغییری در فایل‌های کدهای برنامه، به صورت خودکار برنامه را build کرده و مجددا ارائه می‌دهد.


تکمیل برنامه‌ی وب جهت ارسال پیام‌هایی به کلاینت‌های متصل به آن

پس از افزودن وابستگی‌های مورد نیاز، بازیابی و build برنامه، اکنون نوبت به تعریف یک Hub است، تا از طریق آن بتوان پیام‌هایی را به کلاینت‌های متصل ارسال کرد. به همین جهت یک پوشه‌ی جدید را به نام Hubs به پروژه‌ی وب افزوده و سپس کلاس جدید MessageHub را به صورت ذیل به آن اضافه می‌کنیم:
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

namespace SignalRCore2WebApp.Hubs
{
    public class MessageHub : Hub
    {
        public Task Send(string message)
        {
            return Clients.All.InvokeAsync("Send", message);
        }
    }
}
این کلاس از کلاس پایه Hub مشتق می‌شود. سپس در متد Send آن می‌توان پیام‌هایی را به کلاینت‌های متصل به برنامه ارسال کرد.

پس از تعریف این Hub، نیاز است به کلاس Startup مراجعه کرده و دو تغییر ذیل را اعمال کنیم:
الف) ثبت و معرفی سرویس SignalR
ابتدا باید SignalR را فعالسازی کرد. به همین جهت نیاز است سرویس‌های آن‌را به صورت یکجا توسط متد الحاقی AddSignalR در متد ConfigureServices به نحو ذیل معرفی کرد:
public void ConfigureServices(IServiceCollection services)
{
   services.AddSignalR();
   services.AddMvc();
}

ب) ثبت مسیریابی دسترسی به Hub
پس از تعریف Hub، مرحله‌ی بعدی، مشخص سازی نحوه‌ی دسترسی به آن است. به همین جهت در متد Configure، به نحو ذیل Hub را معرفی کرده و سپس یک path را برای آن مشخص می‌کنیم:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   app.UseSignalR(routes =>
   {
      routes.MapHub<MessageHub>(path: "message");
    });
یعنی اکنون این Hub در آدرس ذیل قابل دسترسی است:
  http://localhost:5000/message
این آدرسی است که در کلاینت Angular، از آن برای اتصال به هاب، استفاده خواهیم کرد.


انتشار پیام‌هایی به تمام کاربران متصل به برنامه

آدرس فوق به تنهایی کار خاصی را انجام نمی‌دهد. از آن جهت اتصال کلاینت‌های برنامه استفاده می‌شود و این کلاینت‌ها پیام‌های رسیده‌ی از طرف برنامه را از این آدرس دریافت خواهند کرد. بنابراین مرحله‌ی بعد، ارسال تعدادی پیام به سمت کلاینت‌ها است. برای این منظور به HomeController برنامه‌ی وب مراجعه کرده و آن‌را به نحو ذیل تغییر می‌دهیم:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using SignalRCore2WebApp.Hubs;

namespace SignalRCore2WebApp.Controllers
{
    public class HomeController : Controller
    {
        private readonly IHubContext<MessageHub> _messageHubContext;
        public HomeController(IHubContext<MessageHub> messageHubContext)
        {
            _messageHubContext = messageHubContext;
        }

        public IActionResult Index()
        {
            return View(); // show the view
        }

        [HttpPost]
        public async Task<IActionResult> Index(string message)
        {
            await _messageHubContext.Clients.All.InvokeAsync("Send", message);
            return View();
        }
    }
}
برای دسترسی به Hubهای تعریف شده می‌توان از سیستم تزریق وابستگی‌ها استفاده کرد. برای این منظور تنها کافی است Hub مدنظر را به عنوان آرگومان جنریک IHubContext تعریف کرد. سپس از طریق آن می‌توان به این context‌، در قسمت‌های مختلف برنامه دسترسی یافت و برای مثال پیام‌هایی را به کاربران ارائه داد.
در این مثال ابتدا View ذیل نمایش داده می‌شود:
@{
    ViewData["Title"] = "Home Page";
}

<form method="post"
      asp-action="Index"
      asp-controller="Home"
      role="form">
  <div class="form-group">
     <label label-for="message">Message: </label>
     <input id="message" name="message" class="form-control"/>
  </div>
  <button class="btn btn-primary" type="submit">Send</button>
</form>
کار آن فرستادن یک پیام به متد Index است. سپس این متد، به کمک context تزریق شده‌ی Hub پیام‌ها، این پیام را به تمام کلاینت‌های متصل ارسال می‌کند.


تکمیل برنامه‌ی کلاینت Angular جهت نمایش پیام‌های رسیده‌ی از طرف سرور

تا اینجا ساختار ابتدایی برنامه‌ی Angular را توسط Angular CLI ایجاد کردیم. اکنون نیاز است وابستگی سمت کلاینت SignalR Core را نصب کنیم. به همین جهت از طریق خط فرمان به پوشه‌ی SignalRCore2Client وارد شده و دستور ذیل را صادر کنید:
 npm install @aspnet/signalr-client --save
پرچم save آن سبب خواهد شد تا این وابستگی علاوه بر نصب، در فایل package.json نیز درج شود.
کلاینت رسمی signalr، هم جاوا اسکریپتی است و هم تایپ‌اسکریپتی. به همین جهت به سادگی توسط یک برنامه‌ی تایپ اسکریپتی Angular قابل استفاده است. کلاس‌های آن‌را در مسیر node_modules\@aspnet\signalr-client\dist\src می‌توانید مشاهده کنید.
در ابتدا، فایل app.component.ts را به نحو ذیل تغییر می‌دهیم:
import { Component, OnInit } from "@angular/core";
import { HubConnection } from "@aspnet/signalr-client";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  hubPath = "http://localhost:5000/message";
  messages: string[] = [];

  ngOnInit(): void {
    const connection = new HubConnection(this.hubPath);
    connection.on("send", data => {
      this.messages.push(data);
    });
    connection.start().then(() => {
      // connection.invoke("send", "Hello");
      console.log("connected.");
    });
  }
}
در اینجا در ابتدا، کلاس HubConnection از ماژول aspnet/signalr-client@ دریافت شده‌است. سپس بر این اساس در ngOnInit، یک وهله از آن که به مسیر Hub تعریف شده‌ی برنامه اشاره می‌کند، ایجاد خواهد شد. هر زمانیکه پیامی از سمت سرور دریافت گردید، این پیام را به لیست messages، که یک آرایه است اضافه می‌کنیم. در آخر برای راه اندازی این اتصال، متد start آ‌ن‌را فراخوانی خواهیم کرد. در اینجا می‌توان یک متد سمت سرور را فراخوانی کرد و یا برقراری اتصال را در کنسول developers مرورگر نمایش داد.
آرایه‌ی messages را به نحو ذیل توسط یک حلقه در قالب این کامپوننت نمایش خواهیم داد:
<div>
  <h1>
    The messages from the server:
  </h1>
  <ul>
    <li *ngFor="let message of messages">
      {{message}}
    </li>
  </ul>
</div>
پس از آن به ریشه‌ی پروژه‌ی کلاینت مراجعه کرده و دستور ذیل را صادر می‌کنیم تا برنامه‌ی Angular ساخته شده و در مرورگر پیش فرض سیستم نمایش داده شود:
  ng serve -o
در این حالت برنامه در آدرس  http://localhost:4200/ قابل دسترسی خواهد بود.


همانطور که مشاهده می‌کنید، پیام خطای ذیل را صادر کرده‌است:
 Failed to load http://localhost:5000/message: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
علت اینجا است که برنامه‌ی Angular بر روی پورت 4200 کار می‌کند و برنامه‌ی وب ما بر روی پورت 5000 تنظیم شده‌است. به همین جهت نیاز است CORS را در برنامه‌ی وب تنظیم کرد تا امکان یک چنین دسترسی صادر شود.
برای این منظور به فایل آغازین برنامه‌ی وب مراجعه کرده و سرویس‌های AddCors را به مجموعه‌ی سرویس‌های برنامه اضافه می‌کنیم:
public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
    services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder
                        .AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials());
            });
    services.AddMvc();
}
پس از آن در متد Configure، این سیاست دسترسی باید مورد استفاده قرار گیرد؛ و گرنه این تنظیمات کار نخواهد کرد. محل قرارگیری آن نیز باید پیش از سایر تنظیمات باشد:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   app.UseCors(policyName: "CorsPolicy");
اکنون اگر مجددا برنامه‌ی Angular را Refresh کنیم، در console توسعه دهندگان مرورگر، مشاهده خواهیم کرد که اتصال برقرار شده‌است:


در آخر برای آزمایش برنامه، به آدرس http://localhost:5000 یا همان برنامه‌ی وب، مراجعه کرده و پیامی را ارسال کنید. بلافاصله مشاهده خواهید کرد که این پیام توسط کلاینت Angular دریافت شده و نمایش داده می‌شود:



کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید: SignalRCore2Sample.zip
برای اجرا آن، ابتدا به پوشه‌ی SignalRCore2WebApp مراجعه کرده و دو فایل bat آن‌را به ترتیب اجرا کنید. اولی وابستگی‌ها‌ی برنامه را بازیابی می‌کند و دومی برنامه را بر روی پورت 5000 ارائه می‌دهد.
سپس به پوشه‌ی SignalRCore2Client مراجعه کرده و در آنجا نیز دو فایل bat ابتدایی آن‌را به ترتیب اجرا کنید. اولی وابستگی‌های برنامه‌ی Angular را بازیابی می‌کند و دومی برنامه‌ی Angular را بر روی پورت 4200 اجرا خواهد کرد.
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت اول - نصب پیشنیازها
نیاز به یک Gulp Task دارد تا تمام فایل‌های پروژه به همراه فایل‌های AngularJS 2.0 یکی شوند؛ به همراه uglify و minify خودکار: «شروع به کار با AngularJS 2.0 و TypeScript - قسمت دوازدهم - توزیع برنامه»