WebSockets.Otp.Redis
0.0.3
dotnet add package WebSockets.Otp.Redis --version 0.0.3
NuGet\Install-Package WebSockets.Otp.Redis -Version 0.0.3
<PackageReference Include="WebSockets.Otp.Redis" Version="0.0.3" />
<PackageVersion Include="WebSockets.Otp.Redis" Version="0.0.3" />
<PackageReference Include="WebSockets.Otp.Redis" />
paket add WebSockets.Otp.Redis --version 0.0.3
#r "nuget: WebSockets.Otp.Redis, 0.0.3"
#:package WebSockets.Otp.Redis@0.0.3
#addin nuget:?package=WebSockets.Otp.Redis&version=0.0.3
#tool nuget:?package=WebSockets.Otp.Redis&version=0.0.3
WebSockets.Otp
A minimal WebSocket library for ASP.NET Core inspired by REPR principles. Provides a clean endpoint-based API for building real-time applications.
Quick Start
1. Define your endpoint
[WsEndpoint("chat/message")]
public class ChatEndpoint : WsEndpoint<ChatMessage, ChatResponse>
{
public override async Task HandleAsync(ChatMessage request, EndpointContext<ChatResponse> context)
{
await context.Send
.Group("general-chat")
.SendAsync(new ChatResponse
{
Username = request.Username,
Message = request.Message,
Timestamp = DateTime.UtcNow
}, default);
}
}
public class ChatMessage
{
public string Username { get; set; }
public string Message { get; set; }
}
public class ChatResponse
{
public string Username { get; set; }
public string Message { get; set; }
public DateTime Timestamp { get; set; }
}
2. Configure services
// Program.cs
builder.Services.AddWsEndpoints();
app.MapEndpoints(
"/ws",
(opt) =>
{
opt.OnConnected = async (context) =>
{
await context.Groups.AddAsync("general-chat", context.ConnectionId);
};
opt.OnDisconnected = async (context) =>
{
await context.Groups.RemoveAsync("general-chat", context.ConnectionId);
};
});
Handshake
The first message sent over a WebSocket connection must be a handshake message. This is required before any endpoint can be invoked.
Client → Server
{"protocol":"json"}
Server → Client
{}
If the first message is not a valid handshake, the server will close the connection. This ensures protocol compatibility and allows for future protocol negotiation.
Endpoint Types
The library supports three endpoint patterns:
1. Simple Endpoint (No request/response)
[WsEndpoint("system/status")]
public class SystemStatusEndpoint : WsEndpoint
{
public override async Task HandleAsync(EndpointContext context)
{
// Handle raw WebSocket messages
var buffer = context.Payload.Span;
// Custom processing logic
}
}
2. Request-only Endpoint (Any type response)
[WsEndpoint("notifications/subscribe")]
public class SubscribeEndpoint : WsEndpoint<SubscribeRequest>
{
public override async Task HandleAsync(SubscribeRequest request, EndpointContext context)
{
await context.Groups.AddAsync("notifications", context.ConnectionId);
await connection.Send
.SendAsync(new
{
Data = "response"
}, default);
}
}
3. Request/Response Endpoint
[WsEndpoint("calculator/add")]
public class AddEndpoint : WsEndpoint<AddRequest, AddResponse>
{
public override async Task HandleAsync(AddRequest request, EndpointContext<AddResponse> context)
{
var result = request.A + request.B;
await context.Send
.Client(context.ConnectionId)
.SendAsync(new AddResponse
{
Result = result,
Operation = "addition"
}, default);
}
}
Advanced Features
1. Dependency Injection and lifetime management
[WsEndpoint("auth/validate", ServiceLifetime.Singleton)]
public class AuthEndpoint : WsEndpoint<AuthRequest, AuthResponse>
{
private readonly IAuthService _authService;
private readonly ILogger<AuthEndpoint> _logger;
public AuthEndpoint(IAuthService authService, ILogger<AuthEndpoint> logger)
{
_authService = authService;
_logger = logger;
}
public override async Task HandleAsync(AuthRequest request, EndpointContext<AuthResponse> context)
{
var isValid = await _authService.ValidateAsync(request.Token);
}
}
2. Group Management
public class ChatEndpoint : WsEndpoint<ChatMessage>
{
public override async Task HandleAsync(ChatMessage request, EndpointContext context)
{
// Add connection to group
await context.Groups.AddAsync("chat-room", context.ConnectionId);
// Send to specific group
await context.Send
.Group("chat-room")
.SendAsync(new { Message = "Welcome!" });
// Send to multiple groups
await context.Send
.Group("chat-room")
.Group("custom")
.SendAsync(new { Message = "Welcome!" });
// Remove from group
await context.Groups.RemoveAsync("chat-room", context.ConnectionId);
}
}
3. Authorization
Authorization can be applied globally (per connection), per endpoint, or both. Endpoint-level attributes are evaluated in addition to global settings.
Global (per connection)
Applied to every endpoint mapped under the same base path:
app.MapEndpoints(
"/ws",
(opt) =>
{
opt.AuthorizationData =
[
new AuthorizeAttribute
{
AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme
}
];
});
Per endpoint
Applied only to the decorated endpoint:
[Authorize(Policy = "ws.chat")]
[WsEndpoint("chat/message/send")]
public class ChatEndpoint : WsEndpoint<ChatMessage>
{
// ...
}
Combined
Global and endpoint-level authorization are additive a connection must satisfy both to reach the endpoint. Use global rules for connection-wide concerns (e.g. authentication scheme) and per-endpoint attributes for fine-grained policies.
4. Distributed Connections
Scale across multiple server instances by backing the connection registry with a shared store. Requires a Redis instance.
Install
dotnet add package WebSockets.Otp.Redis
Setup
Register a Redis multiplexer, then enable the Redis-backed connection manager:
builder.Services.AddSingleton<IConnectionMultiplexer>(
_ => ConnectionMultiplexer.Connect("localhost:6379"));
builder.Services.AddRedisManager();
5. Custom Serializers
By default, the library uses JSON for message serialization. For custom types or alternative formats (e.g. MessagePack, Protobuf, XML), implement the ISerializer interface from WebSockets.Otp.Abstractions.Serializers and register it as a singleton.
Roadmap
- Performance & memory optimization
- Pre/Post processors
- Rate limiting
- Versioning
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- StackExchange.Redis (>= 3.2.0)
- WebSockets.Otp.Abstractions (>= 0.0.1)
-
net8.0
- StackExchange.Redis (>= 3.2.0)
- WebSockets.Otp.Abstractions (>= 0.0.1)
-
net9.0
- StackExchange.Redis (>= 3.2.0)
- WebSockets.Otp.Abstractions (>= 0.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.