ساخت چت روم با Blazor Web Assembly و SignalR قسمت اول: ساخت سرور



در این مقاله دو قسمتی قصد داریم که بوسیله SignalR در سرور ASP Net Core و Blazor Web Assembly یک چت روم بسازیم.


معماری کلی پروژه

برای ساده نگه داشتن پروژه، یک پروژه سرور که ASP Net Core Web Api هست داریم. برای کلاینت یک پروژه Blazor Web Assembly داریم و برای مشترکات بین دو پروژه که شامل مدل ها هست یک پروژه از نوع Class Libraryداریم. معماری کلی پروژه را میتوانید در شکل زیر مشاهده کنید. دقت داشته باشید که دو پروژه Server و Client به پروژه Shared رفرنس دارند.

ساخت و تکمیل سرور

افزودن Identity به سرور

ابتدا پکیج Microsoft.Extensions.Identity.Stores را از طریق Nuget دانلود و نصب میکنیم

از طریق Package Manager نیز میتوانیم با دستور زیر اقدام به نصب این پکیج بکنیم.

Install-Package Microsoft.Extensions.Identity.Stores -Version 5.0.9

سپس به سراغ نصب پکیج Microsoft.AspNetCore.Identity.EntityFrameworkCore می رویم

از طریق Package Manager نیز میتوانیم با دستور زیر اقدام به نصب این پکیج بکنیم.

Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore -Version 5.0.9

ساخت مدل های Identity

پس از نصب Identity به سراغ ساخت مدل های آن می رویم. ابتدا در پروژه سرور یک فولدر با نام IdentityModels ایجاد میکنیم

برای ساخت مدل User به شکل زیر عمل میکنیم

https://gist.github.com/babaktaremi/d26815fdf353aa639b6c1e646d3757a5

سپس سایر مدل های Identity را به شکل زیر میسازیم


https://gist.github.com/babaktaremi/0824da8ba22e90abc7929c853e8c987e
https://gist.github.com/babaktaremi/47feb0145ce818c7815d5d5df8ff1102
https://gist.github.com/babaktaremi/73b60523331c32202bacdece1c8dc5c1
https://gist.github.com/babaktaremi/eb10695641d9c99fd07c18548aa9b35b
https://gist.github.com/babaktaremi/9ee3a6d30eee6a9afa83f6228d40f780


https://gist.github.com/babaktaremi/d91fdecd626f11fc8d07f7bc2999a791


ساخت Database Context

پس از ساخت مدل های Identity به سراغ ساخت ApplicationDbContext می رویم و آن را به شکل زیر می سازیم.ابتدا یک فولدر با نام Context می سازیم و ApplicationDbContext را داخل آن قرار میدهیم.

https://gist.github.com/babaktaremi/305485da35f7387d464af034b8f68a3e


برای استفاده از EF Core در پروژه و ایجاد Migration ها نیازمند پکیج های زیر هستیم که از طریق Package Manager و با دستورات زیر آنها را نصب میکنیم

Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 5.0.9
Install-Package Microsoft.EntityFrameworkCore.Design -Version 5.0.9
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 5.0.9

سپس در متد ConfigureServices کلاس Startup این Database Context را رجیستر میکنیم.

https://gist.github.com/babaktaremi/b2020dcedb9dd9b6622270721d264fd2

سپس با ایجاد Migration جداول مربوطه را میسازیم.


تنظیم Identity

برای تنظیم Identity به شکل زیر عمل میکنیم.ابتدا یک فولدر با نام Extensions می سازیم و سپس یک کلاس داخل آن با نام IdentityConfigurationExtension ایجاد میکنیم.

https://gist.github.com/babaktaremi/e557e618af94920a32a2cf497c78b79e

در اینجا تنها یک پیاده سازی برای User Claim ها داریم که در آن میتوانیم Custom Claim ها را به توکن کاربر اضافه کنیم. پیاده سازی آن به شکل زیر می باشد

https://gist.github.com/babaktaremi/0972dc5eabbaf975584678f9e9e280cb


سپس در متد ConfigureServices کلاس Startup از این کلاس به شکل زیر استفاده میکنیم.

https://gist.github.com/babaktaremi/0f2d47564bb428d71e9080c1f7fac465

افزودن JWT به Identity

برای افزودن JWT به Identity نیازمند پکیج Microsoft.AspNetCore.Authentication.JwtBearer هستیم

این پکیج را از طریق دستور زیر نیز میتوانیم نصب کنیم.

Install-Package Microsoft.AspNetCore.Authentication.JwtBearer -Version 5.0.9

برای تنظیم JWT در فولدر Extensions یک کلاس با نام JWTConfigurationExtension به شکل زیر ایجاد میکنیم.

https://gist.github.com/babaktaremi/d1daabdeaae4c3a579bc2f6f7e979eb6

سپس در متد ConfigureServices کلاس Startup تغییرات زیر را اعمال میکنیم.

https://gist.github.com/babaktaremi/40b72abfc4814b3c589b0ae3f3a27438

در متد Configure کلاس Startup نیز تغییرات زیر را اعمال میکنیم.

https://gist.github.com/babaktaremi/dabef9c1c2432d34484e1837a47b36eb

ایجاد سرویس های مربوط به ساخت JWT Token

برای ایجاد JWT Token ابتدا در پروژه Shared کلاس AccessToken را به شکل زیر میسازیم. ابتدا در پروژه Shared یک پوشه با نام JWT ایجاد میکنیم و در آن کلاس AccessToken را قرار میدهیم. برای استفاده از کلاس JwtSecurityToken نیازمند پکیج System.IdentityModel.Tokens.Jwt که آن را با دستور زیر از طریق Package Manager Console نصب میکنیم.

Install-Package System.IdentityModel.Tokens.Jwt -Version 6.12.2
https://gist.github.com/babaktaremi/349a70882d9404fd8ee7d0386aeeeff2

این کلاس شامل اطلاعاتی است که برای احراز هویت کاربران مورد نیاز است. ابتدا تایپ توکن را بعنوان Bearer تعریف میکنیم و سپس مقدار زمانی که توکن معتبر است را بعنوان ثانیه نشان میدهیم.

در پروژه Server یک فولدر با نام Service ایجاد میکنیم و در آن یک فولدر دیگر با نام Jwt ایجاد میکنیم.

سپس در آن یک اینترفیس با نام IJwtService ایجاد میکنیم که به صورت زیر میباشد.

https://gist.github.com/babaktaremi/eb19489a03f57b3dd649c04f7d362577

سپس کلاس JwtService را به شکل زیر که اینترفیس IJwtService را پیاده سازی میکند مینویسیم.

https://gist.github.com/babaktaremi/6a473e9e0b96e31008787c6268acca57

در این کد ما JWT Token را به صورت Encrypt شده در میاوریم. برای مطالعه بیشتر درباره نحوه Encrypt کردن JWT Token میتوانید به این مقاله از محمد جواد ابراهیمی عزیز مراجعه کنید.

سپس در متد ConfigureServices این سرویس را به شکل زیر رجیستر میکنیم.

https://gist.github.com/babaktaremi/7c5a1d7db7e9223b9fbdb52df366a5db

ایجاد اکشن های Login و Register

ابتدا مدل های Login و Register را میسازیم. در پروژه Shared یک پوشه با نام Account ایجاد میکنیم و دو کلاس LoginViewModel و RegisterViewModel را به شکل زیر میسازیم.

https://gist.github.com/babaktaremi/0d92076e8de6d0687d67f9203ac0e78e
https://gist.github.com/babaktaremi/c4210bee5b68f711a334e127c7688b34

سپس به سراغ ساخت AccountController می رویم. ابتدا این کنترلر را به شکل زیر می سازیم و سپس اکشن های Login و Register را به آن اضافه میکنیم.

https://gist.github.com/babaktaremi/54451345e7c880b40630c2af3890e5b3

برای ساخت اکشن Register به شکل زیر عمل میکنیم

https://gist.github.com/babaktaremi/963b1de8a3e1361d132169092094faa0

برای ساخت اکشن Login نیز به شکل زیر عمل میکنیم.

https://gist.github.com/babaktaremi/0e770471df62788851f21fc7430bf498

سپس این دو اکشن را در پنل Swagger تست میکنیم.

ساخت جدول Chat History

ابتدا در پروژه Server یک فولدر با نام Models ایجاد میکنیم. و در آن یک کلاس با نام ChatHistory ایجاد میکنیم. این جدول رابطه یک به چند با جدول User دارد.

https://gist.github.com/babaktaremi/75e117772d096d7bf918172ca24cac82

سپس تغییر زیر را در کلاس User ایجاد میکنیم.

https://gist.github.com/babaktaremi/d81b8f19d063b695b6cb88b5e3416c86

سپس تغییر زیر در کلاس ApplicationDbContext ایجاد میکنیم.

https://gist.github.com/babaktaremi/7a409bdaf33a8ca827a80ad10e42d7d0


سپس Migration مربوط به ایجاد جدول ChatHistory را ایجاد میکنیم و در نهایت دیتابیس را آپدیت میکنیم.


ساخت Chat Hub و Chat Controller

ابتدا در پروژه Shared یک فولدر با نام Chat ایجاد میکنیم و کلاس های مربوط به Chat Hub را در آن ایجاد میکنیم.

در این فولدر 3 کلاس با نام های UserMessageViewModel و UserIsTypingViewModel و UserJoinedChatViewModel و UserNewMessageViewModel قرار دارند.

از UserMessageViewModel برای نشان دادن پیام ها و همچنین Notify کردن سایر کاربران هنگام ایجاد پیام جدید استفاده میکنیم.

از UserIsTypingViewModel برای Notify کردن سایر کاربران هنگامی که یک کاربر در حال تایپ است استفاده میکنیم

از UserJoinedChatViewModel برای Notify کردن سایر کاربران هنگامی که یک کاربر به چت روم وصل میشود استفاده میکنیم.

از UserNewMessageViewModel هنگامی که کاربر یک پیام جدید به سرور میفرستد استفاده میکنیم.

https://gist.github.com/babaktaremi/cf0ed0347ce9afcd73c4a93fff2cca08
https://gist.github.com/babaktaremi/0b06acd0092b5d697d9507a14282b09f
https://gist.github.com/babaktaremi/c8c0bb4e0ac072b97c3f3929c5ae86b4
https://gist.github.com/babaktaremi/cdda68f7f469a0deeae997560aff304b


سپس در پروژه Server یک پوشه با نام Hub ایجاد میکنیم. در آن یک اینترفیس با نام IChatHub به شکل زیر اضافه میکنیم.

https://gist.github.com/babaktaremi/d00ad7ac5001e2e827202851711fa338

سپس ChatHub را به شکل زیر ایجاد میکنیم.

https://gist.github.com/babaktaremi/be32491c841dd4259bd1e59c9a6a0fb3


متد OnConnectedAsync را Override کرده ایم و در خط 8 به محض اینکه یک کاربر به هاب متصل میشود نام کاربری آن را به سایر کلاینت ها نشان میدهیم.

سپس به Hub یک متد با نام OnNewMessage اضافه میکنیم که پیام های جدید را گرفته، به دیتابیس اضافه کرده و سپس سایر کلاینت ها را Notify کند.

https://gist.github.com/babaktaremi/b4c2c7f411a88596fe6866a92f59599a

سپس برای اینکه به سایر کاربران نشان دهیم که کاربر در حال تایپ است متد زیر به هاب اضافه میکنیم.

https://gist.github.com/babaktaremi/f7f4b49ef4da631e79eed6e6897b5d4e

در متد ConfigureServices کلاس Startup سیگنال آر را به شکل زیر به پروژه اضافه میکنیم.

https://gist.github.com/babaktaremi/9f0a97ebb7cc1c420adf67456aace61b

سپس ChatHub را به شکل زیر به URL مپ میکنیم.

https://gist.github.com/babaktaremi/ccaff63cf6a8d476b19c2b4614b27648

در کلاس JwtConfigurationExtension برای اینکه ChatHub بتواند با JWTکار کند باید تغییرات زیر را اعمال کنیم. چون که در Web Socket نمیتوان مقدار AccessToken را از Header خواند، باید آن را تحت یک QueryString به سرور پاس دهیم. پس کلاس JwtConfigurationExtension به شکل زیر در میاید.

https://gist.github.com/babaktaremi/6140381ba56ba3b6b46557d186450bc3

حال به سراغ ساخت ChatController میرویم. شکل ابتدایی ChatController به صورت زیر است.

https://gist.github.com/babaktaremi/367f822f404ddefad89d302f65c4d901


در این کنترلر ما نیاز به یک اکشن داریم که پیام هایی که تا به حال منتشر شده است را دریافت کند

https://gist.github.com/babaktaremi/01622cc87290d2cb479176484b7466a5

تنظیمات CORS

برای اینکه Web Api ساخته شده سمت Client در دسترس باشد، نیاز داریم که CORS را در آن فعال کنیم. برای فعال سازی CORS به شکل زیر عمل میکنیم.

ابتدا در متد ConfigureServices کلاس Startup قطعه کد زیر را اضافه میکنیم

https://gist.github.com/babaktaremi/e4e0433b1dc8a7ffcbd010b0761665f9

برای اینکه بتوانیم از JWT Token در SignalR استفاده کنیم باید از متد AllowCredentials استفاده کنیم. سپس در متد Configure کلاس Startup قطعه کد زیر را اضافه میکنیم.

جمع بندی

در این قسمت از مقاله یک سرور برای اپلیکیشن چت روم با استفاده از Identity ، JWT Authentication و SignalR ساختیم. در قسمت بعد به سراغ ساخت بخش کلاینت با استفاده از Blazor Web Assembly و Signal R Client می رویم و در نهایت یک اپلیکیشن چت روم به صورت Real-Time خواهیم داشت

مقالات بیشتر در دات نت زوم

https://t.me/DotNetZoom