Pemvedr.Common.SignalR 0.1.20241117.2
#«Pemvedr.Common.SignalR»
Structure
Multiple Clients
When multiple signalr clients need to be registered use the AddClient
Example: Typed Hub & client
Shared interfaces
Define interface for the methods handled by the client (which are called by the hub) and the methods handled by the hub (which are called by the client)
public interface IMethodsHandledByClient
{
Task MessageReceived(string[] messages);
}
public interface IMethodsHandledByHub
{
Task SendMessageToAll(string[] message);
}
Typed C# hub
Create a hub class, derive it from Hub<T> to have the Clients.All return a proxy of T to make it typesafe. Implement the IMethodsHandledByHub to be sure to handle all supported methods which can be called by the clients. ChatsHub is registered with Signalr which wil by convention look for the methods with the same name.
public class ChatsHub : Hub<IMethodsHandledByClient>, IMethodsHandledByHub
{
public Task SendMessageToAll(string[] messages) => Clients.All.MessageReceived(messages);
}
register signal-r and hub
Add to Startup.cs
public void ConfigureServices(IServiceCollection services) => services.AddPemvedrSignalR();
public void Configure(IApplicationBuilder app) => app
.UsePemvedrSignalR()
.UseEndpoints(endpoints => endpoints.MapHub<ChatsHub>("/hub/chats"))
Usage
When client make calls to the hub the call is handled by the chatshub.
When the hub needs to call the methods handled by the client, inject the IHubContext<ChatsHub, IMethodsHandledByClient>. This is registered by the MapHub method. The second interface IMethodsHandledByClient is to make the Clients.«All|Others» typesafe.
public class SignalrModel : PageModel
{
private readonly IHubContext<ChatsHub, IMethodsHandledByClient> _hubContext;
public SignalrModel(IHubContext<ChatsHub, IMethodsHandledByClient> hubContext) => _hubContext = hubContext;
public IActionResult OnGet() => Page();
[BindProperty] public string Message { get; set; } = string.Empty;
public IActionResult OnPostAsHubAsync()
{
_ = _hubContext.Clients.All.MessageReceived(new[] { Message });
return Page();
}
}
Typed C# client
Implement the handler class
public class ChatsHandler : IMethodsHandledByClient
{
private readonly ISerializeToHumanReadable _serializeToHumanRadable;
private readonly ILog _log;
public ChatsHandler(ILog log, ISerializeToHumanReadable serializeToHumanRadable)
{
_log = log.For(this);
_serializeToHumanRadable = serializeToHumanRadable;
}
public Task MessageReceived(string[] messages) => _log.This(() => $"messages received: {_serializeToHumanRadable.Serialize(messages)}").Information();
}
register signal-r and hubclient
For each client call AddClient<«discriminator»>(...). It can be called without the generic part when there is only one client, otherwise the disriminator can be any class to separate the clients.
AddClientMethods<IMethodsHandledByClient, ChatsHandler>() registers ChatsHandler as the handler for the calls from the hub to this client.
.AddHubMethods<IMethodsHandledByHub>() registers a dynamicproxy which implements IMethodsHandledByHub to be able to calls the hub in a typesafe manner.
Add to Startup.cs
public void ConfigureServices(IServiceCollection services) => services
.AddPemvedrSignalR(builder => builder
.AddClient(_ => new Uri("https://localhost:6001/hub/chats"), hubBuilder => hubBuilder
.AddHubMethods<IMethodsHandledByHub>()
.AddClientMethods<IMethodsHandledByClient, ChatsHandler>()));
public void Configure(IApplicationBuilder app) => app.UsePemvedrSignalR()
Usage
When hub make calls to the client, the call is handled by the ChatsHandler. When the client needs to call the methods handled by the hub, inject the IMethodsHandledByHub
public class SignalrModel : PageModel
{
private readonly IMethodsHandledByHub _hubProxy;
public SignalrModel(IMethodsHandledByHub hubProxy) => _hubProxy = hubProxy;
public IActionResult OnGet() => Page();
[BindProperty] public string Message { get; set; } = string.Empty;
public IActionResult OnPostAsClientAsync()
{
_ = _hubProxy.SendMessageToAll(new[] { Message });
return Page();
}
}
Semi-Typed Javascript
Mark the method interfaces so swashbuckle will export the methodnames as enums
[WantMyMethodNamesExportedAsEnum]
public interface IMethodsHandledByClient
{
...
}
[WantMyMethodNamesExportedAsEnum]
public interface IMethodsHandledByHub
{
...
}
Create a hubscontroller to let swashbuckle the interfaces so it will export them.
[ApiController]
[Route("api/[controller]")]
public class HubsController : ControllerBase
{
[HttpGet("chats/methods")]
public (IMethodsHandledByClient, IMethodsHandledByHub)? GetChat() => null;
}
Add a script to generate code based on the swagger.json swashbuckle produces. set NODE_TLS_REJECT_UNAUTHORIZED=0 will disable certificate validation due to the selfsigned .net development certificate.
{
...
"scripts": {
...
"api": "set NODE_TLS_REJECT_UNAUTHORIZED=0&npx openapi-typescript-codegen --input https://localhost:6001/swagger/v1/swagger.json --output ./src/api"
},
...
}
start the swashbuckle backend an generate the javascript code npm run api
after that the generated enum can be used to setup the javascript signal-r client like
signalr.on(Pemvedr_Common_Testbed_IMethodsHandledByClient.MESSAGE_RECEIVED, (message:string) => this.addMessage(message))
It might be necessary to configure the CORS of the backend to accept the signalr specific headers
public class Startup
{
public void ConfigureServices(IServiceCollection services) => services
.AddForRecommendedOrder(
options => options
.UseCallingAssemblyAsApplicationName()
.WithRecommendedSecurityOptions(securityOptions => securityOptions
.WithCors(builder => builder
.WithHeaders("x-requested-with")
.WithHeaders("x-signalr-user-agent")
)))
}
Development hints
none
See also
none
No packages depend on Pemvedr.Common.SignalR.
.NET 8.0
- Pemvedr.Common.ExtensionMethods (>= 0.6.20240401.1)
- Pemvedr.Common.Logging (>= 0.1.20240401.1)
- Pemvedr.Common.Utilities (>= 0.5.20241030.1)
- Castle.Core (>= 5.1.1)
- Castle.Core.AsyncInterceptor (>= 2.1.0)
- Microsoft.AspNetCore.SignalR.Client (>= 8.0.10)
- Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson (>= 8.0.10)
| Version | Downloads | Last updated |
|---|---|---|
| 0.1.20241117.3 | 9 | 11/17/2024 |
| 0.1.20241117.2 | 6 | 11/17/2024 |
| 0.1.20241102.1 | 8 | 11/02/2024 |
| 0.1.20241031.4 | 7 | 10/31/2024 |
| 0.1.20241030.1 | 7 | 10/30/2024 |
| 0.1.20240903.1 | 8 | 09/02/2024 |
| 0.1.20240602.1 | 6 | 06/02/2024 |
| 0.1.20240401.1 | 6 | 04/01/2024 |
| 0.1.20221229.2 | 11 | 12/28/2022 |
| 0.1.20221220.4 | 7 | 12/20/2022 |
| 0.1.20220327.1 | 5 | 05/15/2022 |
| 0.1.20220105.1 | 8 | 05/15/2022 |
| 0.1.20211222.1 | 6 | 05/15/2022 |
| 0.1.20211221.1 | 5 | 05/15/2022 |
| 0.1.20211219.2 | 5 | 05/15/2022 |
| 0.1.20211216.2 | 5 | 05/15/2022 |