ساخت چت روم با 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.9Install-Package Microsoft.EntityFrameworkCore.Design -Version 5.0.9Install-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