SignalR Create Group chat using the IhubContext service

osyris 236 Reputation points
2021-07-25T23:59:29.24+00:00

I have succesfully create a chat page using SignalR
now i would like to create a more professional chat
Like a help desk system
so when a user send a message in the chat it should give a notification to a (manager) page
and were only 1 person can enter the room

I have used the IHubContext service this is my simple code so far:

    [Route("api")]
    [ApiController]
    public class ChatController : ControllerBase
    {
        private readonly IHubContext<ChatHub, IChatClient> _chathub;

        public ChatController(IHubContext<ChatHub, IChatClient> chathub)
        {
            _chathub = chathub;
        }

        [HttpPost("messages")]
        public async Task Post([FromBody]ChatMessage message)
        {
            string name = "jack";

                await _chathub.Clients.All.ReceiveMessage(name, message.Message);
        }
    }

and from the front end:

receiving messages:

const Connection = new HubConnectionBuilder()
            .withUrl('https://localhost:44370/hubs/chat')
            .build();
        try{
      Connection.start().then(result => { console.log("connected")});
      Connection.on("ReceiveMessage",(user, message) => {
        const li = document.createElement("li");
        li.textContent = `${user}: ${message}`;
        document.getElementById("messagelist").appendChild(li);})
      }
      catch(err){
      console.log("something went wrong while connection: ", err)
      }

sending messages:

 try {
            await fetch('https://localhost:44370/api/messages',{
                method: 'POST',
                body: JSON.stringify({
                    message: newmessage
                }),
                headers: {'Content-Type': 'application/json'}
            }).catch((error) => {
                console.error('Error:', error);
              });

I have tried to look into the microsoft documents on how to achive what i want but
its not very clear on how to achive this through the IHubContext service

hopefully someone can help me out to better understand to achive what i want

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,555 questions
ASP.NET
ASP.NET
A set of technologies in the .NET Framework for building web applications and XML web services.
3,483 questions
{count} votes

Accepted answer
  1. Tiny Wang-MSFT 2,646 Reputation points Microsoft Vendor
    2021-07-28T08:09:23.43+00:00

    Hi @osyris , I followed these 2 documents to create a sample to show you my idea on this case, hope it helps.

    My thought is when the user start a chat, you can call a service like JoinGroup in my sample. Here, you need to set a unique group name, then save it in some where.

    Next you need to made the manager page to update the notification, and when the manager click the new notification, he will be added to the group(call the JoinGroup method again with the same groupname), then these 2 people can start a conversation.

    118631-image.png

    ChatHub.cs

    using Microsoft.AspNetCore.SignalR;  
    using System.Threading.Tasks;  
      
    namespace WebApplication1.Hubs  
    {  
        public class ChatHub : Hub  
        {  
            public async Task SendMessage(string user, string message)  
            {  
                await Clients.All.SendAsync("ReceiveMessage", user, message);  
            }  
      
            public Task JoinGroup(string group)  
            {  
                //user call this method with a group name as the parameter  
                //if(user call this method){   
                //    do some action to save group name  
                //    return Groups.AddToGroupAsync(Context.ConnectionId, group);  
                //}else if(manager call this method){   
                //    get group name  
                //    return Groups.AddToGroupAsync(Context.ConnectionId, group);  
                //}  
                return Groups.AddToGroupAsync(Context.ConnectionId, group);  
            }  
      
            public Task SendMessageToGroup(string groupname, string sender, string message)  
            {  
                return Clients.Group(groupname).SendAsync("ReceiveMessage", sender, message);  
            }  
        }  
    }  
    

    chat.js

    "use strict";  
      
    var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();  
      
    //Disable send button until connection is established  
    document.getElementById("sendButton").disabled = true;  
      
    connection.on("ReceiveMessage", function (user, message) {  
        var li = document.createElement("li");  
        document.getElementById("messagesList").appendChild(li);  
        li.textContent = `${user} says ${message}`;  
    });  
      
    connection.start().then(function () {  
        document.getElementById("sendButton").disabled = false;  
    }).catch(function (err) {  
        return console.error(err.toString());  
    });  
      
    //document.getElementById("sendButton").addEventListener("click", function (event) {  
    //    var user = document.getElementById("userInput").value;  
    //    var message = document.getElementById("messageInput").value;  
    //    connection.invoke("SendMessage", user, message).catch(function (err) {  
    //        return console.error(err.toString());  
    //    });  
    //    event.preventDefault();  
    //});  
      
    document.getElementById("sendButton").addEventListener("click", function (event) {  
        var user = document.getElementById("userInput").value;  
        var message = document.getElementById("messageInput").value;  
        connection.invoke("SendMessageToGroup","PrivateGroup", user, message).catch(function (err) {  
            return console.error(err.toString());  
        });  
        event.preventDefault();  
    });  
      
    document.getElementById("btnPrivateGroup").addEventListener("click", function (event) {  
        var groupName = "PrivateGroup";  
        connection.invoke("JoinGroup", groupName).catch(function (err) {  
            return console.error(err.toString());  
            //if user call joingroup successfully, then write code to update the manager page's notification  
        });  
        event.preventDefault();  
    });  
    

    If the answer is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.
    Best Regards,
    TinyWang

    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. osyris 236 Reputation points
    2021-07-29T23:39:48.533+00:00

    Thank you very much this helped me allot
    I have one last question.

    I have one project were i want the chat to be at the right bottom corner like a help desk
    I know how to code it but how do i deal with the url.

    in a normal asp net core react api project the back and en the front-end share the same host number
    but with the SingalR project the front-end framework(react) has a url of :http://localhost:3000/
    and the back-end: https://localhost:44370/

    in a normal asp net core react api project the startup.cs would look like this:

    public void ConfigureServices(IServiceCollection services)
            {
    //other codes..
    
    services.AddSpaStaticFiles(configuration =>
                {
                    configuration.RootPath = "React/build";
    
                });
    
            }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
    
    //other codes..
    app.UseSpaStaticFiles();
    //other codes..
    
    app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllerRoute(
                        name: "default",
                        pattern: "{ controller}/{ action = Index}/{id?}");
    
                });
                app.UseSpa(spa =>
                {
                    spa.Options.SourcePath = "React";
    
                    if (env.IsDevelopment())
                    {
                        spa.UseReactDevelopmentServer(npmScript: "start");
                    }
    
            });
    

    the project with with SignalR looks like this:

    public void ConfigureServices(IServiceCollection services)
            {
    
    services.AddSignalR();
    
                services.AddCors(options =>
                {
                    options.AddPolicy("ReactChat", policy =>
                    {
                        policy.AllowAnyHeader()
                            .AllowAnyMethod()
                            .WithOrigins("http://localhost:3000")
                            .AllowCredentials();
    
                    });
                });
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
    
    // other codes..
    
    app.UseCors("ReactChat");
    
    //other codes..
    
    app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                    endpoints.MapHub<ChatHub>("/hubs/chat");
                });
    

    for the ConfigureServices i will just add the services but how do i deal with the Configure section


  2. Tiny Wang-MSFT 2,646 Reputation points Microsoft Vendor
    2021-07-30T10:24:07.16+00:00

    119384-reactcode.txtHi @osyris , see attachment for react code. And according to your description, we only need to add cors policy in asp.net core project. And other code are the same with above sample.

    startup.cs

    using Microsoft.AspNetCore.Builder;  
    using Microsoft.AspNetCore.Hosting;  
    using Microsoft.Extensions.Configuration;  
    using Microsoft.Extensions.DependencyInjection;  
    using Microsoft.Extensions.Hosting;  
    using WebApplication1.Hubs;  
      
    namespace WebApplication1  
    {  
        public class Startup  
        {  
            public Startup(IConfiguration configuration)  
            {  
                Configuration = configuration;  
            }  
      
            public IConfiguration Configuration { get; }  
      
            // This method gets called by the runtime. Use this method to add services to the container.  
            public void ConfigureServices(IServiceCollection services)  
            {  
                services.AddRazorPages();  
                services.AddCors(options =>  
                {  
                    options.AddPolicy(name: "ReactChat",  
                                      builder =>  
                                      {  
                                          builder.AllowAnyMethod()  
                                          .WithOrigins("http://localhost:3000")  
                                          .AllowAnyHeader()  
                                          .AllowCredentials();  
                                      });  
                });  
                services.AddSignalR();  
      
                  
            }  
      
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.  
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)  
            {  
                if (env.IsDevelopment())  
                {  
                    app.UseDeveloperExceptionPage();  
                }  
                else  
                {  
                    app.UseExceptionHandler("/Error");  
                    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.  
                    app.UseHsts();  
                }  
      
                app.UseHttpsRedirection();  
                app.UseStaticFiles();  
      
                app.UseRouting();  
      
                app.UseCors("ReactChat");  
      
                //app.UseAuthorization();  
      
                app.UseEndpoints(endpoints =>  
                {  
                    endpoints.MapRazorPages();  
                    endpoints.MapHub<ChatHub>("/chatHub");  
                });  
            }  
        }  
    }  
    

    119306-image.png


    If the answer is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.
    Best Regards,
    TinyWang


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.