Questo esempio è una semplice chat realizzata in C# .NET 6 per mostrare il funzionamento e la configurazione di SignalR.
.NET 6 include nativamente il supporto per SignalR.

Creazione Progetto

Per creare il progetto in Visual Studio selezionare il template ASP.NET Core Web App con framework .NET 6.0 (Long-term support e HTTPS abilitato
ASP.NET Core Web AppASP.NET Core Web App

Libreria JavaScript

Il passo successivo è aggiungere la libreria JavaScript SignalR.

Sul progetto tasto destro Add / Client-Side Library... e nella form
  • selezionare il provider unpkg
  • inserire la library @microsoft/signalr@latest
  • selezionare Choose specific files
  • selezionare in dist/browser solo signalr.js e signalr.min.js
  • inserirecome target wwwroot/js/signalr
JavaScript libraryJavaScript library

Hub e Message

Per permettere la comunicazione client/server va aggiunta una classe ChatHub che eredita da Microsoft.AspNetCore.SignalR.Hub
C#: Hubs/ChatHub.cs
using Microsoft.AspNetCore.SignalR;
using Sgart.Net.SignalR.DTO;

namespace Sgart.Net.SignalR.Hubs
{
    /// <summary>
    /// gestisce la comunicazione client/server
    /// </summary>
    public class ChatHub : Hub
    {
        /// <summary>
        /// il nome del metodo è il valore da usare 
        /// lato JavaScript connection.invoke("SendMessage", data)
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task SendMessage(ChatDTO data)
        {
            // applico un timestamp certo
            data.Date = DateTime.UtcNow;

            await Clients.All.SendAsync("ReceiveMessage", data);
        }
    }
}
e una classe che rappresenta il messaggio da scambiare ChatDTO
C#: DTO/ChatDTO.cs
namespace Sgart.Net.SignalR.DTO
{
    /// <summary>
    /// rappresenta il messaggio da scambiare con SignalR
    /// </summary>
    public class ChatDTO
    {
        public string? ClientId { get; set; }
        public DateTime Date { get; set; }
        public string? User { get; set; }
        public string? Message { get; set; }
    }
}

Configurazione SignalR

La configurazione di SignalR avviene richiamando il metodo AddSignalR e MapHub per il mapping del canale di comunicazione
C#: Program.cs
using Sgart.Net.SignalR.Hubs;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();

// aggiungo il servizio SignalR
builder.Services.AddSignalR();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    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.MapRazorPages();

// mapping del canali di comunicazione
app.MapHub<ChatHub>("/chatHub");

app.Run();

Interfaccia utente

Nella pagina di Index va inserito l'HTML che rappresenta la chat
HTML: Pages/Index.cshtml
@page

<header class="contact">
    <section>
        User: <input type="text" id="user-input" value="User 1" />
    </section>
</header>

<section class="messages" id="messages-list">
</section>

<form id="message-form" class="form-input">
    <input type="text" id="message-input" autocomplete="off" />
    <button type="submit" id="send-button">Send</button>
</form>

<script src="~/js/signalr/dist/browser/signalr.js"></script>
<script src="~/js/chat.js"></script>
[/page]
la pagina di '''Layout'' si semplifica l'[tag]HTML[/tag] 
[code=cs,Pages/Shared/_Layouts.cshtml]
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Sgart.Net.SignalR</title>
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    @*<link rel="stylesheet" href="~/Sgart.Net.SignalR.styles.css" asp-append-version="true" />*@
</head>
<body>
    @RenderBody()

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>
e si completa il tutto con un CSS
CSS: wwwroot/css/site.css
:root {
  --bg-color-black: #000000;
  --bg-color-gray: #444444;
  --msg-me-bg-color: #f0ad4e;
  --msg-me-text-color: #000000;
  --msg-others-bg-color: #600000;
  --msg-others-text-color: #eeeeee;
  --msg-date-color: #888888;
  --msg-user-color: #cccccc;
}

html, body {
  height: 100%;
  font: 100%/1.5em "Arial", sans-serif;
  padding: 0;
  margin: 0;
}

html, body, * {
  box-sizing: border-box;
}

body {
  flex-direction: column;
}

body, header, .msg, form {
  display: flex;
}

header, blockquote {
  padding: .5em;
  margin: 0;
}

header {
  background-color: #374455;
  color: white;
  padding-right: 0;
  line-height: 0.5em;
}

.messages, input {
  background-color: black;
}

input {
  padding-left: .325em;
  border: none;
  color: white;
  flex: 1;
}

.messages {
  padding: 1em;
  margin: 0;
  flex: 1;
  overflow: auto;
}

.msg {
  position: relative;
  margin-bottom: 2em;
  color: #fff;
  display: flex;
  flex-direction: column;
}

.msg-header {
  display: flex;
  text-align: right;
}

.msg-date {
  color: var(--msg-date-color);
  font-size: .9em;
}

.msg-user {
  color: var(--msg-user-color);
  font-size: .9em;
  font-weight: bold;
}

.msg-body {
  display: flex;
  min-height: 3em;
}

.msg-my {
  padding-left: 20%;
}

.msg-my .msg-header{
  flex-direction: row-reverse;
}
.msg-my .msg-date {
  margin-right: 2em;
}
.msg-my .msg-body {
  background-color: var(--msg-me-bg-color);
  color: var(--msg-me-text-color);
  border-radius: .5em;
}

.msg-others {
  padding-right: 20%;
}

.msg-others .msg-header {
  flex-direction: row;
}
.msg-others .msg-date {
  margin-left: 2em;
}
.msg-others .msg-body {
  background-color: var(--msg-others-bg-color);
  color: var(--msg-others-text-color);
  border-radius: .5em;
}

.form-input {
  background-color: #727b80;
  padding: 1px;
  min-height: 40px;
}

  .form-input input {
    height: 100%;
    outline: none;
  }

  .form-input button {
    flex-grow: 0;
    text-transform: uppercase;
    background-color: var(--bg-color-black);
    color: var(--msg-me-bg-color);
    font-weight: bold;
    border: 0;
  }

    .form-input button:hover {
      background-color: var(--bg-color-gray);
    }
questo è il risultato
Interfaccia utenteInterfaccia utente

Client

Per completare la chat è necessario aggiungere del codice JavaScript per gestire l'invio (sendMessage) è la ricezione (ReceiveMessage) dei messaggi
JavaScript: wwwroot/js/chat.js
"use strict";

const htmlEncode = msg => {
  if (msg === undefined || msg === null)
    return null;
  var node = document.createTextNode(msg);
  return document.createElement("a").appendChild(node).parentNode.innerHTML.replace(/'/g, "&#39;").replace(/"/g, "&#34;");
};

try {

  // Disable the send button until connection is established.
  document.getElementById("send-button").disabled = true;

  const clientUniqueId = Math.floor(Math.random() * 999999999) + "-" + Date.now();  // TODO: da migliorare

  document.getElementById("user-input").value = "User " + Math.floor(Math.random() * 9999);

  // salvo il riferimento alla text box del messaggi
  const elmMessage = document.getElementById("message-input");
  // imposto il focus
  elmMessage.value = "";
  elmMessage.focus();

  //---------------------------------------------------------------------------------------
  // setup chat

  const connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();

  connection.start()
    .then(() => document.getElementById("send-button").disabled = false)
    .catch(err => console.error(err.toString()));

  // receive message

  connection.on("ReceiveMessage", data => {
    try {
      const user = htmlEncode(data.user);
      const date = htmlEncode(data.date);
      const message = htmlEncode(data.message);

      const template = `<div class="msg-header"><span class="msg-user">${user}</span><span class="msg-date">${date}</span></div>`
        + `<div class="msg-body"><blockquote>${message}</blockquote></div>`;

      const wrapper = document.createElement("div");
      wrapper.className = data.clientId === clientUniqueId ? "msg msg-my" : "msg msg-others";
      wrapper.innerHTML = template;

      const wrapperMessages = document.getElementById("messages-list");
      
      wrapperMessages.appendChild(wrapper);

      // scroll bottom to show last message
      wrapperMessages.scrollTop = wrapperMessages.scrollHeight;

    } catch (ex) {
      console.error(ex, "ReceiveMessage");
    }
  });

  // send message

  const sendMessage = event => {
    event.preventDefault();
    try {
      if (document.getElementById("send-button").disabled === true) {
        return;
      }

      if (elmMessage.value.length === 0) {
        return;
      }

      const data = {
        clientId: clientUniqueId,
        user: document.getElementById("user-input").value,
        message: elmMessage.value
      };

      // SendMessage = nome del metodo nella classe Hub
      connection.invoke("SendMessage", data)
        .catch(err => console.error(err.toString()));

      elmMessage.value = "";
      elmMessage.focus();
    } catch (ex) {
      console.error(ex, "sendMessage");
    }
  };

  // aggancio l'evento di submit al form
  document.getElementById("message-form").addEventListener("submit", sendMessage);

} catch (ex) {
  console.error(ex, "chat");
}

Il codice completo è disponibile su GitHub - Sgart.Net.SignalR.
Potrebbe interessarti anche: