Dmitri Shimanski c851815a2c Refactor WebSocket routing and error handling logic
Replaces `Dictionary` with `ConcurrentDictionary` for thread-safe WebSocket route management and improves error logging with added debug assertions. Also fixes duplicate registrations, enhances dependency injection, updates package references, and adjusts WebSocket attribute structure for better extensibility and usage.
2025-05-25 03:32:33 +03:00
2025-03-29 02:59:34 +03:00
2025-03-29 02:34:08 +03:00
2025-03-29 02:34:08 +03:00
2025-03-29 02:34:08 +03:00
2025-03-29 02:34:08 +03:00

New WebSocket routing system

Features

  • ASP.NET Core-style WebSocket routing 🛣️
  • Method-based endpoint handlers (Like in ASP!) 🎯
  • Simple integration with existing applications

Installation

Add the NuGet package to your project:

dotnet add package yawaflua.WebSockets

Quick Start

1. Create WebSocket Controller

public class ChatController : WebSocketController
{
    [WebSocket("/chat")]
    public override async Task OnMessageAsync(
        IWebSocket webSocket, 
        HttpContext httpContext)
    {
        await webSocket.SendAsync("Message!");
    }
}

2. Configure Services

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddControllers()
        .SettingUpWebSockets(); // ← Add WebSocket routing
        
    services.AddSingleton<ChatController>();
}

3. Enable Middleware

public void Configure(IApplicationBuilder app)
{
    app.ConnectWebSockets(); // ← Add WebSocket handling
    
    app.UseRouting();
    app.UseEndpoints(endpoints => endpoints.MapControllers());
}

Advanced Usage

Parameterized Routes

public class NotificationsController : WebSocketController
{
    [WebSocket("/notifications/{userId}")]
    public override async Task OnMessageAsync(
        IWebSocket webSocket,
        HttpContext httpContext)
    {
        var userId = httpContext.Request.RouteValues["userId"];
        // Handle user-specific notifications
    }
}

Method-Level Routes

[WebSocket("/game")]
public class GameController : WebSocketController
{
    [WebSocket("join/{roomId}")]
    public async Task JoinRoom(IWebSocket webSocket, HttpContext context)
    {
        // Handle room joining
    }

    [WebSocket("leave/{roomId}")]
    public async Task LeaveRoom(IWebSocket webSocket, HttpContext context)
    {
        // Handle room leaving
    }
}

Providing dependencies in controller

[WebSocket("/chat")]
public class ChatController : WebSocketController
{
    public static DbContext dbContext;

    public ChatController(DbContext dbContext)
    {
        ChatController.dbContext = dbContext;
    }
    
    [WebSocket("join/{roomId}")]
    public async Task JoinRoom(WebSocket webSocket, HttpContext context)
    {
        await dbContext.Add(...);
        // Next your logic etc
    }

    [WebSocket("leave/{roomId}")]
    public async Task LeaveRoom(WebSocket webSocket, HttpContext context)
    {
        // Handle room leaving
    }
}

Run any code on connection to WebSocket

services.AddSingleton(new WebSocketConfig()
{
    OnOpenHandler = async (socket, context) =>
    {
        if (socket.WebSocketManager!.GetAllClients().Count(k =>
                Equals(k.ConnectionInfo!.RemoteIpAddress!.MapToIPv4(), 
                    socket.Client.ConnectionInfo!.RemoteIpAddress!.MapToIPv4())) >= 3)
        {
            await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Too many users");
        }
        Console.WriteLine($"{socket.Client.Id} has been connected to {socket.Client.Path}");
    }
})

Work with c=connected users from any point at your code!

public class MyCoolService 
{
    private IWebSocketManager _manager;
    public MyCoolService(IWebSocketManager manager)
    {
        _manager = manager;
    }
    
    public async Task DoSomething()
    {
        await _manager.Broadcast(k => k.Path == "/my/cool/endpoint", "Hello!");
    }
}

// DependencyInjection should provide IWebSocketManager to builder
services.AddSingleton<MyCoolService>(); 

Lifecycle Management

  1. Connection - Automatically handled by middleware
  2. Message Handling - Implement OnMessageAsync
  3. Cleanup - Dispose resources in IDisposable interface

Best Practices

  1. Keep Controllers Light - Move business logic to services
  2. Use Dependency Injection - Inject services via constructor
  3. Handle Exceptions - Wrap operations in try/catch blocks
  4. Manage State - Use HttpContext.Items for request-scoped data

Troubleshooting

No Route Handling?

  • Verify controller registration in DI:
    services.AddSingleton<YourController>();
    

Connection Issues?

  • Ensure middleware order:
    app.ConnectWebSockets(); // Must be before UseRouting/UseEndpoints
    

Parameters Not Working?

  • Check route template syntax:
    [WebSocket("/correct/{paramName}")] // ✓
    [WebSocket("/wrong/{param-name}")]  // ✗
    

License

Apache license - Free for commercial and personal use.

Description
Small library for websocket routing system
Readme Apache-2.0 71 KiB
Languages
C# 100%