معرفی JSON Web Token
دو روش کلی و پرکاربرد اعتبارسنجی سمت سرور، برای برنامههای سمت کاربر وب وجود دارند:
الف) Cookie-Based Authentication که پرکاربردترین روش بوده و در این حالت به ازای هر درخواست، یک کوکی جهت اعتبارسنجی کاربر به سمت سرور ارسال میشود (و برعکس).
ب) Token-Based Authentication که بر مبنای ارسال یک توکن امضاء شده به سرور، به ازای هر درخواست است.
مزیتهای استفادهی از روش مبتنی بر توکن چیست؟
• Cross-domain / CORS: کوکیها و CORS آنچنان با هم سازگاری ندارند؛ چون صدور یک کوکی وابستهاست به دومین مرتبط به آن و استفادهی از آن در سایر دومینها عموما پذیرفته شده نیست. اما روش مبتنی بر توکن، وابستگی به دومین صدور آنرا ندارد و اصالت آن بر اساس روشهای رمزنگاری تصدیق میشود.
• بدون حالت بودن و مقیاس پذیری سمت سرور: در حین کار با توکنها، نیازی به ذخیرهی اطلاعات، داخل سشن سمت سرور نیست و توکن موجودیتی است خود شمول (self-contained). به این معنا که حاوی تمام اطلاعات مرتبط با کاربر بوده و محل ذخیرهی آن در local storage و یا کوکی سمت کاربر میباشد.
• توزیع برنامه با CDN: حین استفاده از روش مبتنی بر توکن، امکان توزیع تمام فایلهای برنامه (جاوا اسکریپت، تصاویر و غیره) توسط CDN وجود دارد و در این حالت کدهای سمت سرور، تنها یک API ساده خواهد بود.
• عدم در هم تنیدگی کدهای سمت سرور و کلاینت: در حالت استفادهی از توکن، این توکن میتواند از هرجایی و هر برنامهای صادر شود و در این حالت نیازی نیست تا وابستگی ویژهای بین کدهای سمت کلاینت و سرور وجود داشته باشد.
• سازگاری بهتر با سیستمهای موبایل: در حین توسعهی برنامههای بومی پلتفرمهای مختلف موبایل، کوکیها روش مطلوبی جهت کار با APIهای سمت سرور نیستند. تطابق یافتن با روشهای مبتنی بر توکن در این حالت سادهتر است.
• CSRF: از آنجائیکه دیگر از کوکی استفاده نمیشود، نیازی به نگرانی در مورد حملات CSRF نیست. چون دیگر برای مثال امکان سوء استفادهی از کوکی فعلی اعتبارسنجی شده، جهت صدور درخواستهایی با سطح دسترسی شخص لاگین شده وجود ندارد؛ چون این روش کوکی را به سمت سرور ارسال نمیکند.
• کارآیی بهتر: حین استفادهی از توکنها، به علت ماهیت خود شمول آنها، رفت و برگشت کمتری به بانک اطلاعاتی صورت گرفته و سرعت بالاتری را شاهد خواهیم بود.
• امکان نوشتن آزمونهای یکپارچگی سادهتر: در حالت استفادهی از توکنها، آزمودن یکپارچگی برنامه، نیازی به رد شدن از صفحهی لاگین را ندارد و پیاده سازی این نوع آزمونها سادهتر از قبل است.
• استاندارد بودن: امروزه همینقدر که استاندارد JSON Web Token را پیاده سازی کرده باشید، امکان کار با انواع و اقسام پلتفرمها و کتابخانهها را خواهید یافت.
اما JWT یا JSON Web Token چیست؟
JSON Web Token یا JWT یک استاندارد وب است (RFC 7519) که روشی فشرده و خود شمول (self-contained) را جهت انتقال امن اطلاعات، بین مقاصد مختلف را توسط یک شیء JSON، تعریف میکند. این اطلاعات، قابل تصدیق و اطمینان هستند؛ از اینرو که به صورت دیجیتال امضاء میشوند. JWTها توسط یک کلید مخفی (با استفاده از الگوریتم HMAC) و یا یک جفت کلید خصوصی و عمومی (توسط الگوریتم RSA) قابل امضاء شدن هستند.
در این تعریف، واژههایی مانند «فشرده» و «خود شمول» بکار رفتهاند:
- «فشرده بودن»: اندازهی شیء JSON یک توکن در این حالت کوچک بوده و به سادگی از طریق یک URL و یا پارامترهای POST و یا داخل یک HTTP Header قابل ارسال است و به دلیل کوچک بودن این اندازه، انتقال آن نیز سریع است.
- «خود شمول»: بار مفید (payload) این توکن، شامل تمام اطلاعات مورد نیاز جهت اعتبارسنجی یک کاربر است؛ تا دیگر نیازی به کوئری گرفتن هر بارهی از بانک اطلاعاتی نباشد (در این روش مرسوم است که فقط یکبار از بانک اطلاعاتی کوئری گرفته شده و اطلاعات مرتبط با کاربر را امضای دیجیتال کرده و به سمت کاربر ارسال میکنند).
چه زمانی بهتر است از JWT استفاده کرد؟
اعتبارسنجی: اعتبارسنجی یک سناریوی متداول استفادهی از JWT است. زمانیکه کاربر به سیستم لاگین کرد، هر درخواست بعدی او شامل JWT خواهد بود که سبب میشود کاربر بتواند امکان دسترسی به مسیرها، صفحات و منابع مختلف سیستم را بر اساس توکن دریافتی، پیدا کند. برای مثال روشهای «Single Sign On» خود را با JWT انطباق دادهاند؛ از این جهت که سربار کمی را داشته و همچنین به سادگی توسط دومینهای مختلفی قابل استفاده هستند.
انتقال اطلاعات: توکنهای با فرمت JWT، روش مناسبی جهت انتقال اطلاعات امن بین مقاصد مختلف هستند؛ زیرا قابل امضاء بوده و میتوان اطمینان حاصل کرد که فرستنده دقیقا همانی است که ادعا میکند و محتوای ارسالی دست نخوردهاست.
ساختار یک JWT به چه صورتی است؟
JWTها دارای سه قسمت جدا شدهی با نقطه هستند؛ مانند xxxxx.yyyyy.zzzzz و شامل header، payload و signature میباشند.
الف) Header
Header عموما دارای دو قسمت است که نوع توکن و الگوریتم مورد استفادهی توسط آن را مشخص میکند:
{ "alg": "HS256", "typ": "JWT" }
ب) payload
payload یا «بار مفید» توکن، شامل claims است. منظور از claims، اطلاعاتی است در مورد موجودیت مدنظر (عموما کاربر) و یک سری متادیتای اضافی. سه نوع claim وجود دارند:
Reserved claims: یک سری اطلاعات مفید و از پیش تعیین شدهی غیراجباری هستند؛ مانند:
iss یا صادر کنند (issuer)، exp یا تاریخ انقضاء، sub یا عنوان (subject) و aud یا مخاطب (audience)
Public claims: میتواند شامل اطلاعاتی باشد که توسط IANA JSON Web Token Registry پیشتر ثبت شدهاست و فضاهای نام آنها تداخلی نداشته باشند.
Private claims: ادعای سفارشی هستند که جهت انتقال دادهها بین مقاصد مختلف مورد استفاده قرار میگیرند.
یک نمونهی payload را در اینجا ملاحظه میکنید:
{ "sub": "1234567890", "name": "John Doe", "admin": true }
ج) signature
یک نمونه فرمول محاسبهی امضای دیجیتال پیام JWT به صورت ذیل است:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
یک نمونه مثال تولید این نوع توکنها را در آدرس https://jwt.io میتوانید بررسی کنید.
در این سایت اگر به قسمت دیباگر آن مراجعه کنید، برای نمونه قسمت payload آن قابل ویرایش است و تغییرات را بلافاصله در سمت چپ، به صورت انکد شده نمایش میدهد.
یک نکتهی مهم: توکنها امضاء شدهاند؛ نه رمزنگاری شده
همانطور که عنوان شد، توکنها از سه قسمت هدر، بار مفید و امضاء تشکیل میشوند (header.payload.signature). اگر از الگوریتم HMACSHA256 و کلید مخفی shhhh برای امضای بار مفید ذیل استفاده کنیم:
{ "sub": "1234567890", "name": "Ado Kukic", "admin": true }
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFkbyBLdWtpYyIsImFkbWluIjp0cnVlLCJpYXQiOjE0NjQyOTc4ODV9.Y47kJvnHzU9qeJIN48_bVna6O0EDFiMiQ9LpNVDFymM
البته امکان رمزنگاری توسط JSON Web Encryption نیز پیش بینی شدهاست (JWE).
از JWT در برنامهها چگونه استفاده میشود؟
زمانیکه کاربر، لاگین موفقی را به سیستم انجام میدهد، یک توکن امن توسط سرور صادر شده و با فرمت JWT به سمت کلاینت ارسال میشود. این توکن باید به صورت محلی در سمت کاربر ذخیره شود. عموما از local storage برای ذخیرهی این توکن استفاده میشود؛ اما استفادهی از کوکیها نیز منعی ندارد. بنابراین دیگر در اینجا سشنی در سمت سرور به ازای هر کاربر ایجاد نمیشود و کوکی سمت سروری به سمت کلاینت ارسال نمیگردد.
سپس هر زمانیکه کاربری قصد داشت به یک صفحه یا محتوای محافظت شده دسترسی پیدا کند، باید توکن خود را به سمت سرور ارسال نماید. عموما اینکار توسط یک header سفارشی Authorization به همراه Bearer schema صورت میگیرد و یک چنین شکلی را دارد:
Authorization: Bearer <token>
نگاهی به محل ذخیره سازی JWT و نکات مرتبط با آن
محل متداول ذخیرهی JWT ها، در local storage مرورگرها است و در اغلب سناریوها نیز به خوبی کار میکند. فقط باید دقت داشت که local storage یک sandbox است و محدود به دومین جاری برنامه و از طریق برای مثال زیر دامنههای آن قابل دسترسی نیست. در این حالت میتوان JWT را در کوکیهای ایجاد شدهی در سمت کاربر نیز ذخیره کرد که چنین محدودیتی را ندارند. اما باید دقت داشت که حداکثر اندازهی حجم کوکیها 4 کیلوبایت است و با افزایش claims ذخیره شدهی در یک JWT و انکد شدن آن، این حجم ممکن است از 4 کیلوبایت بیشتر شود. بنابراین باید به این نکات دقت داشت.
امکان ذخیره سازی توکنها در session storage مرورگرها نیز وجود دارد. session storage بسیار شبیه است به local storage اما به محض بسته شدن مرورگر، پاک میشود.
اگر از local storage استفاده میکنید، حملات Cross Site Request Forgery در اینجا دیگر مؤثر نخواهند بود. اما اگر به حالت استفادهی از کوکیها برای ذخیرهی توکنها سوئیچ کنید، این مساله همانند قبل خواهد بود و مسیر است. در این حالت بهتر است طول عمر توکنها را تاحد ممکن کوتاه تعریف کنید تا اگر اطلاعات آنها فاش شد، به زودی بیمصرف شوند.
انقضاء و صدور مجدد توکنها به چه صورتی است؟
توکنهای بدون حالت، صرفا بر اساس بررسی امضای پیام رسیده کار میکنند. به این معنا که یک توکن میتواند تا ابد معتبر باقی بماند. برای رفع این مشکل باید exp یا تاریخ انقضای متناسبی را به توکن اضافه کرد. برای برنامههای حساس این عدد میتواند 15 دقیقه باشد و برای برنامههای کمتر حساس، چندین ماه.
اما اگر در این بین قرار به ابطال سریع توکنی بود چه باید کرد؟ (مثلا کاربری را در همین لحظه غیرفعال کردهاید)
یک راه حل آن، ثبت رکوردهای تمام توکنهای صادر شده در بانک اطلاعاتی است. برای این منظور میتوان یک فیلد id مانند را به توکن اضافه کرد و آنرا صادر نمود. این idها را نیز در بانک اطلاعاتی ذخیره میکنیم. به این ترتیب میتوان بین توکنهای صادر شده و کاربران و اطلاعات به روز آنها ارتباط برقرار کرد. در این حالت برنامه علاوه بر بررسی امضای توکن، میتواند به لیست idهای صادر شده و ذخیره شدهی در دیتابیس نیز مراجعه کرده و اعتبارسنجی اضافهتری را جهت باطل کردن سریع توکنها انجام دهد. هرچند این روش دیگر آنچنان stateless نیست، اما با دنیای واقعی سازگاری بیشتری دارد.
حداکثر امنیت JWTها را چگونه میتوان تامین کرد؟
- تمام توکنهای خود را با یک کلید قوی، امضاء کنید و این کلید تنها باید بر روی سرور ذخیره شده باشد. هر زمانیکه سرور توکنی را از کاربر دریافت میکند، این سرور است که باید کار بررسی اعتبار امضای پیام رسیده را بر اساس کلید قوی خود انجام دهد.
- اگر اطلاعات حساسی را در توکنها قرار میدهید، باید از JWE یا JSON Web Encryption استفاده کنید؛ زیرا JWTها صرفا دارای امضای دیجیتال هستند و نه اینکه رمزنگاری شده باشند.
- بهتر است توکنها را از طریق ارتباطات غیر HTTPS، ارسال نکرد.
- اگر از کوکیها برای ذخیره سازی آنها استفاده میکنید، از HTTPS-only cookies استفاده کنید تا از Cross-Site Scripting XSS attacks در امان باشید.
- مدت اعتبار توکنهای صادر شده را منطقی انتخاب کنید.
یه سوال کلی اینکه ما برای طراحی کلیت برناممون باید برای هر صفحه یه layout طراحی کنیم و کدهای اون رو تو activity اون بنویسیم؟
نقشه راه توسعه NET. تا سال 2023
در ماه میسال جاری میلادی مایکروسافت نقشهی توسعه NET. رو تا سال 2023 منتشر کرد و این نقشه نه فقط مربوط به NET Core. بلکه همهی خانوادهی NET. هست. این بروزرسانی علاوه بر ویندوز خانوادهی بزرگی از بسترهای متنوع و متفاوتی مثل لینوکس، سیستمعامل مک، IOS، اندروید، سیستمعاملهای تلویزیون، سیستمعامل ساعت اپل، Web Assembly، ابزارهای متنوع IoT، و کلی بستر متفاوت دیگه رو شامل میشه.
برای اسکرول زیبا و با افکت میتونید ازش استفاده کنید
اما من در پروژهای که به دلیل تعداد زیاد ستونهای جدول از overflow-x:scroll استفاده کرده بودیم که در ویندوز و مک و اندروید برای اسکرول هیچ مشکلی نداشتیم و هم اسکرول عمودی و هم افقی انجام میشد اما در IOS متاسفانه اینگونه نبود و با تغییرات زیاد Css هم نتوانستیم اسکرول افقی را انجام دهیم که به کمک کتابخانه این خبر توانستیم اسکرول را پیاده سازی کنیم و در آخر آن را هم راست به چپ کنیم.
- آخرین نگارش NET 5 SDK. را از اینجا دریافت و نصب کنید (حتما SDK باشد و نه runtime).
- اگر میخواهید با ویژوال استودیو کار کنید، نیاز است حداقل نگارش 16.7 یا بالاتر را نصب کرده باشید. به همین جهت پیش از ادامهی بحث، از منوی Help، گزینهی Check For Updates را انتخاب کرده و حتما آخرین به روز رسانیهای موجود را نصب کنید. بنابراین برای کار با C# 9.0 توسط VS، حتما باید آخرین نگارش 2019 آن، به همراه تمام به روز رسانیهای ممکن بر روی سیستم شما نصب باشند؛ در غیراینصورت امکان کار با آنرا حداقل توسط VS نخواهید داشت.
- در زمان نگارش این مطلب چون هنوز نگارش نهایی NET 5 SDK. ارائه نشدهاست، نیاز است به منوی Tools در ویژوال استودیو مراجعه کرده و با انتخاب «Preview Features» آن، گزینهی «Use previews of the .NET Core SDK» را فعال کنید. پس از آن، یکبار هم نیاز است VS را بسته و راه اندازی مجدد نمائید.
پس از نصب پیشنیازهای لازم، اکنون فایل csproj را به صورت زیر ویرایش کنید:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net5.0</TargetFramework> <LangVersion>9.0</LangVersion> </PropertyGroup> </Project>
تنظیمات مورد نیاز C# 9.0 در پروژههای کتابخانهای
در این نوع پروژهها، علاوه بر نصب پیشنیازهای یاد شده، نیاز است TargetFramework را به حداقل netstandard2.1 تنظیم کرد (و یا حتی net5.0 هم در اینجا کار میکند):
<TargetFramework>netstandard2.1</TargetFramework> <LangVersion>9</LangVersion>
تنظیمات مورد نیاز C# 9.0 در VSCode
برای کار با VSCode، تنها کافی است آخرین نگارش SDK و آخرین نگارش افزونهی #C آنرا نصب کنید و TargetFramework و LangVersion را همانطور که عنوان شد، تنظیم نمائید. این روش سادهترین و کم حجمترین روش کار با C# 9.0 است.
تنظیمات مورد نیاز C# 9.0 در Rider
اگر میخواهید برای کار با C# 9.0 از محصولات Jetbrains استفاده کنید، نیاز است حداقل نگارش 2020.3 آنها را نصب کنید که در این زمان هنوز در مرحلهی پیشنمایش (ReSharper 2020.3 EAP or Rider 2020.3 EAP) به سر میبرند.
من از چه روشی استفاده میکنم؟!
VS کامل بر روی سیستم من نصب نیست؛ هیچ نگارشی از آن! عمدهی کارهای من توسط VS Code و افزونهی #C آن انجام میشوند و هر از چندگاهی، یکبار هم توسط Rider چون به همراه ReSharper توکار است، کنترل کیفیت میشوند (در حد بررسی گزارشهای ReSharper آن). البته افزونهی Roslynator برای VS Code هم موجود است و بسیاری از قابلیتهای ReSharper را در VSCode نیز مهیا میکند.
نگاهی به SignalR Clients
مصرف کنندگان یک Hub میتوانند انواع و اقسام برنامههای کلاینت مانند jQuery Clients و یا حتی یک برنامه کنسول ساده باشند و همچنین Hubهای دیگر نیز قابلیت استفاده از این امکانات Hubهای موجود را دارند. تیم SignalR امکان استفاده از Hubهای آنرا در برنامههای دات نت 4 به بعد، برنامههای WinRT، ویندوز فون 8، سیلورلایت 5، jQuery و همچنین برنامههای CPP نیز مهیا کردهاند. به علاوه گروههای مختلف نیز با توجه به سورس باز بودن این مجموعه، کلاینتهای iOS Native، iOS via Mono و Android via Mono را نیز به این لیست اضافه کردهاند.
بررسی کلاینتهای jQuery
با توجه به پروتکل مبتنی بر JSON سیگنالآر، استفاده از آن در کتابخانههای جاوا اسکریپتی همانند jQuery نیز به سادگی مهیا است. برای نصب آن نیاز است در کنسول پاور شل نوگت، دستور زیر را صادر کنید:
PM> Install-Package Microsoft.AspNet.SignalR.JS
با استفاده از افزونه SignalR jQuery، به دو طریق میتوان به یک Hub اتصال برقرار کرد:
الف) استفاده از فایل proxy تولید شده آن (این فایل، در زمان اجرای برنامه تولید میشود و یا امکان استفاده از آن به کمک ابزارهای کمکی نیز وجود دارد)
نمونهای از آنرا در قسمت قبل ملاحظه کردید؛ همان فایل تولید شده در مسیر /signalr/hubs برنامه. به نوعی به آن Service contract نیز گفته میشود (ارائه متادیتا و قراردادهای کار با یک سرویس Hub). این فایل همانطور که عنوان شد به صورت پویا در زمان اجرای برنامه ایجاد میشود.
امکان تولید آن توسط برنامه کمکی signalr.exe نیز وجود دارد؛ برای دریافت آن میتوان از طریق NuGet اقدام کرد (بسته Microsoft.AspNet.SignalR.Utils) که نهایتا در پوشه packages قرار خواهد گرفت. نحوه استفاده از آن نیز به صورت زیر است:
Signalr.exe ghp http://localhost/
ب) بدون استفاده از فایل proxy و به کمک روش late binding (انقیاد دیر هنگام)
برای کار با یک Hub از طریق jQuery مراحل ذیل باید طی شوند:
1) ارجاعی به Hub باید مشخص شود.
2) روالهای رخدادگردان تنظیم گردند.
3) اتصال به Hub برقرار گردد.
4) متدی فراخوانی شود.
در اینجا باید دقت داشت که امکانات Hub به صورت خواص
$.connection
$.connection.chatHub
خوب، تا اینجا فرض بر این است که یک پروژه خالی ASP.NET را آغاز و سپس فرمان نصب Microsoft.AspNet.SignalR.JS را نیز همانطور که عنوان شد، صادر کردهاید. در ادامه یک فایل ساده html را به نام chat.htm، به این پروژه جدید اضافه کنید (برای استفاده از کتابخانه جاوا اسکریپتی SignalR الزامی به استفاده از صفحات کامل پروژههای وب نیست).
<!DOCTYPE> <html> <head> <title></title> <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script> <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script> <script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script> </head> <body> <div> <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" /> <ul id="messages"> </ul> </div> <script type="text/javascript"> $(function () { var chat; $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ میکند chat = $.connection.chat; //این نام مستعار پیشتر توسط ویژگی نام هاب تنظیم شده است chat.client.hello = function (message) { //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است //به این ترتیب سرور میتواند کلاینت را فراخوانی کند $("#messages").append("<li>" + message + "</li>"); }; $.connection.hub.start(/*{ transport: 'longPolling' }*/); // فاز اولیه ارتباط را آغاز میکند $("#send").click(function () { // Hub's `SendMessage` should be camel case here chat.server.sendMessage($("#txtMsg").val()); }); }); </script> </body> </html>
توضیحات:
همانطور که ملاحظه میکنید ابتدا ارجاعاتی به jquery و jquery.signalR-1.0.1.min.js اضافه شدهاند. سپس نیاز است مسیر دقیق فایل پروکسی هاب خود را نیز مشخص کنیم. اینکار با تعریف مسیر signalr/hubs انجام شده است.
<script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script>
سپس ارجاعی به هاب تعریف شده، تعریف گردیده است. اگر از قسمت قبل به خاطر داشته باشید، توسط ویژگی HubName، نام chat را برگزیدیم. بنابراین connection.chat ذکر شده دقیقا به این هاب اشاره میکند.
سپس سطر chat.client.hello مقدار دهی شده است. متد hello، متدی dynamic و تعریف شده در سمت هاب برنامه است. به این ترتیب میتوان به پیامهای رسیده از طرف سرور گوش فرا داد. در اینجا، این پیامها، به li ایی با id مساوی messages اضافه میشوند.
سپس توسط فراخوانی متد connection.hub.start، فاز negotiation شروع میشود. در اینجا حتی میتوان نوع transport را نیز صریحا انتخاب کرد که نمونهای از آن را به صورت کامنت شده جهت آشنایی با نحوه تعریف آن مشاهده میکنید. مقادیر قابل استفاده در آن به شرح زیر هستند:
- webSockets - forverFrame - serverSentEvents - longPolling
اکنون به صورت جداگانه یکبار برنامه hub را در مرورگر باز کنید. سپس بر روی فایل chat.htm کلیک راست کرده و گزینه مشاهده آن را در مرورگر نیز انتخاب نمائید (گزینه View in browser منوی کلیک راست).
خوب! پروژه کار نمیکند! برای اینکه مشکلات را بهتر بتوانید مشاهده کنید نیاز است به JavaScript Console مرورگر خود مراجعه نمائید. برای مثال در مرورگر کروم دکمه F12 را فشرده و برگه Console آنرا باز کنید. در اینجا اعلام میکند که فاز negotiation قابل انجام نیست؛ چون مسیر پیش فرضی را که انتخاب کرده است، همین مسیر پروژه دومی است که اضافه کردهایم (کلاینت ما در پروژه دوم قرار دارد و نه در همان پروژه اول هاب).
برای اینکه مسیر دقیق hub را در این حالت مشخص کنیم، سطر زیر را به ابتدای کدهای جاوا اسکریپتی فوق اضافه نمائید:
$.connection.hub.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم
using System; using System.Web; using System.Web.Routing; using Microsoft.AspNet.SignalR; namespace SignalR02 { public class Global : HttpApplication { protected void Application_Start(object sender, EventArgs e) { // Register the default hubs route: ~/signalr RouteTable.Routes.MapHubs(new HubConfiguration { EnableCrossDomain = true }); } } }
SignalR: Auto detected cross domain url. jquery.signalR-1.0.1.min.js:10 SignalR: Negotiating with 'http://localhost:1072/signalr/negotiate'. jquery.signalR-1.0.1.min.js:10 SignalR: SignalR: Initializing long polling connection with server. jquery.signalR-1.0.1.min.js:10 SignalR: Attempting to connect to 'http://localhost:1072/signalr/connect?transport=longPolling&connectionToken…NRh72omzsPkKqhKw2&connectionData=%5B%7B%22name%22%3A%22chat%22%7D%5D&tid=3' using longPolling. jquery.signalR-1.0.1.min.js:10 SignalR: Longpolling connected jquery.signalR-1.0.1.min.js:10
در برگه شبکه، مطابق شکل فوق، امکان آنالیز اطلاعات رد و بدل شده مهیا است. برای مثال در حالتیکه سرور پیام دریافتی را به کلیه کلاینتها ارسال میکند، نام متد و نام هاب و سایر پارامترها در اطلاعات به فرمت JSON آن به خوبی قابل مشاهده هستند.
یک نکته:
اگر از ویندوز 8 (یعنی IIS8) و VS 2012 استفاده میکنید، برای استفاده از حالت Web socket، ابتدا فایل وب کانفیگ برنامه را باز کرده و در قسمت httpRunTime، مقدار ویژگی targetFramework را بر روی 4.5 تنظیم کنید. اینبار اگر مراحل negotiation را بررسی کنید در همان مرحله اول برقراری اتصال، از روش Web socket استفاده گردیده است.
تمرین 1
به پروژه ساده و ابتدایی فوق یک تکست باکس دیگر به نام Room را اضافه کنید؛ به همراه دکمه join. سپس نکات قسمت قبل را در مورد الحاق به یک گروه و سپس ارسال پیام به اعضای گروه را پیاده سازی نمائید. (تمام نکات آن با مطلب فوق پوشش داده شده است و در اینجا باید صرفا فراخوانی متدهای عمومی دیگری در سمت هاب، صورت گیرد)
تمرین 2
در انتهای قسمت دوم به نحوه ارسال پیام از یک هاب به هابی دیگر اشاره شد. این MonitorHub را ایجاد کرده و همچنین یک کلاینت جاوا اسکریپتی را نیز برای آن تهیه کنید تا بتوان اتصال و قطع اتصال کلیه کاربران سیستم را مانیتور و مشاهده کرد.
پیاده سازی کلاینت jQuery بدون استفاده از کلاس Proxy
در مثال قبل، از پروکسی پویای مهیای در آدرس signalr/hubs استفاده کردیم. در اینجا قصد داریم، بدون استفاده از آن نیز کار برپایی کلاینت را بررسی کنیم.
بنابراین یک فایل جدید html را مثلا به نام chat_np.html به پروژه دوم برنامه اضافه کنید. سپس محتویات آنرا به نحو زیر تغییر دهید:
<!DOCTYPE> <html> <head> <title></title> <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script> <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script> </head> <body> <div> <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" /> <ul id="messages"> </ul> </div> <script type="text/javascript"> $(function () { $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ میکند var connection = $.hubConnection(); connection.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم var proxy = connection.createHubProxy('chat'); proxy.on('hello', function (message) { //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است //به این ترتیب سرور میتواند کلاینت را فراخوانی کند $("#messages").append("<li>" + message + "</li>"); }); $("#send").click(function () { // Hub's `SendMessage` should be camel case here proxy.invoke('sendMessage', $("#txtMsg").val()); }); connection.start(); }); </script> </body> </html>
کلاینتهای دات نتی SignalR
تا کنون Solution ما حاوی یک پروژه Hub و یک پروژه وب کلاینت جیکوئری است. به همین Solution، یک پروژه کلاینت کنسول ویندوزی را نیز اضافه کنید.
سپس در خط فرمان پاور شل نوگت دستور زیر را صادر نمائید تا فایلهای مورد نیاز به پروژه کنسول اضافه شوند:
PM> Install-Package Microsoft.AspNet.SignalR.Client
پس از نصب آن اگر به پوشه packages مراجعه کنید، نگارشهای مختلف آنرا مخصوص سیلورلایت، دات نتهای 4 و 4.5، WinRT و ویندوز فون8 نیز میتوانید در پوشه Microsoft.AspNet.SignalR.Client ملاحظه نمائید. البته در ابتدای نصب، انتخاب نگارش مناسب، بر اساس نوع پروژه جاری به صورت خودکار صورت میگیرد.
مدل برنامه نویسی آن نیز بسیار شبیه است به حالت عدم استفاده از پروکسی در حین استفاده از jQuery که در قسمت قبل بررسی گردید و شامل این مراحل است:
1) یک وهله از شیء HubConnection را ایجاد کنید.
2) پروکسی مورد نیاز را جهت اتصال به Hub از طریق متد CreateProxy تهیه کنید.
3) رویدادگردانها را همانند نمونه کدهای جاوا اسکریپتی قسمت قبل، توسط متد On تعریف کنید.
4) به کمک متد Start، اتصال را آغاز نمائید.
5) متدها را به کمک متد Invoke فراخوانی نمائید.
using System; using Microsoft.AspNet.SignalR.Client.Hubs; namespace SignalR02.WinClient { class Program { static void Main(string[] args) { var hubConnection = new HubConnection(url: "http://localhost:1072/signalr"); var chat = hubConnection.CreateHubProxy(hubName: "chat"); chat.On<string>("hello", msg => { Console.WriteLine(msg); }); hubConnection.Start().Wait(); chat.Invoke<string>("sendMessage", "Hello!"); Console.WriteLine("Press a key to terminate the client..."); Console.Read(); } } }
نکته مهم
کلیه فراخوانیهایی که در اینجا ملاحظه میکنید غیرهمزمان هستند.
به همین جهت پس از متد Start، متد Wait ذکر شدهاست تا در این برنامه ساده، پس از برقراری کامل اتصال، کار invoke صورت گیرد و یا زمانیکه callback تعریف شده توسط متد chat.On فراخوانی میشود نیز این فراخوانی غیرهمزمان است و خصوصا اگر نیاز است رابط کاربری برنامه را در این بین به روز کنید باید به نکات به روز رسانی رابط کاربری از طریق یک ترد دیگر دقت داشت.