Skip to content

yawaflua/WebSockets

Repository files navigation

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}");
    }
})

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.