mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-03-25 10:31:01 +01:00
7.2 KiB
7.2 KiB
WebSocket Implementation - Final Architecture Summary
✅ REFACTORED TO EXTENSION METHODS PATTERN
The WebSocket implementation has been restructured to follow the exact same pattern as WireMock.Net.ProtoBuf, using extension methods instead of modifying core classes.
📐 Architecture Pattern
Before (Incorrect)
WireMock.Net.Minimal/
├── RequestBuilders/Request.WebSocket.cs ❌ Direct modification
└── ResponseBuilders/Response.WebSocket.cs ❌ Direct modification
After (Correct - Following ProtoBuf Pattern)
WireMock.Net.WebSockets/
├── RequestBuilders/IRequestBuilderExtensions.cs ✅ Extension methods
└── ResponseBuilders/IResponseBuilderExtensions.cs ✅ Extension methods
🔌 Extension Methods Pattern
Request Builder Extensions
public static class IRequestBuilderExtensions
{
public static IRequestBuilder WithWebSocketPath(this IRequestBuilder requestBuilder, string path)
public static IRequestBuilder WithWebSocketSubprotocol(this IRequestBuilder requestBuilder, params string[] subProtocols)
public static IRequestBuilder WithCustomHandshakeHeaders(this IRequestBuilder requestBuilder, params (string Key, string Value)[] headers)
}
Response Builder Extensions
public static class IResponseBuilderExtensions
{
public static IResponseBuilder WithWebSocketHandler(this IResponseBuilder responseBuilder, Func<WebSocketHandlerContext, Task> handler)
public static IResponseBuilder WithWebSocketHandler(this IResponseBuilder responseBuilder, Func<WebSocket, Task> handler)
public static IResponseBuilder WithWebSocketMessageHandler(this IResponseBuilder responseBuilder, Func<WebSocketMessage, Task<WebSocketMessage?>> handler)
public static IResponseBuilder WithWebSocketKeepAlive(this IResponseBuilder responseBuilder, TimeSpan interval)
public static IResponseBuilder WithWebSocketTimeout(this IResponseBuilder responseBuilder, TimeSpan timeout)
public static IResponseBuilder WithWebSocketMessage(this IResponseBuilder responseBuilder, WebSocketMessage message)
}
📦 Project Dependencies
WireMock.Net.WebSockets
<ProjectReference Include="..\WireMock.Net.Shared\WireMock.Net.Shared.csproj" />
- Only Dependency: WireMock.Net.Shared
- External Packages: None (zero dependencies)
- Target Frameworks: netstandard2.1, net462, net6.0, net8.0
WireMock.Net.Minimal
<!-- NO WebSocket dependency -->
<ProjectReference Include="..\WireMock.Net.Shared\WireMock.Net.Shared.csproj" />
- WebSockets is completely optional
- No coupling to WebSocket code
WireMock.Net (main package)
<ProjectReference Include="../WireMock.Net.WebSockets/WireMock.Net.WebSockets.csproj" />
- Includes WebSockets for .NET 3.1+ when needed
✨ Benefits of Extension Method Pattern
- ✅ Zero Coupling - WebSocket code is completely separate
- ✅ Optional Dependency - Users can opt-in to WebSocket support
- ✅ Clean API - No modifications to core Request/Response classes
- ✅ Discoverable - Extension methods appear naturally in IntelliSense
- ✅ Maintainable - All WebSocket code lives in WebSockets project
- ✅ Testable - Can be tested independently
- ✅ Consistent - Matches ProtoBuf, GraphQL, and other optional features
📝 Usage Example
// Extension methods automatically available when WebSockets package is included
server
.Given(Request.Create()
.WithPath("/ws")
.WithWebSocketSubprotocol("chat") // ← Extension method
)
.RespondWith(Response.Create()
.WithWebSocketHandler(async ctx => {}) // ← Extension method
.WithWebSocketKeepAlive(TimeSpan.FromSeconds(30)) // ← Extension method
);
🗂️ File Structure
src/WireMock.Net.WebSockets/
├── WireMock.Net.WebSockets.csproj
├── GlobalUsings.cs
├── README.md
├── Models/
│ ├── WebSocketMessage.cs
│ ├── WebSocketHandlerContext.cs
│ └── WebSocketConnectRequest.cs
├── Matchers/
│ └── WebSocketRequestMatcher.cs
├── ResponseProviders/
│ └── WebSocketResponseProvider.cs
├── RequestBuilders/
│ └── IRequestBuilderExtensions.cs ✅ Extension methods
└── ResponseBuilders/
└── IResponseBuilderExtensions.cs ✅ Extension methods
✅ Project References
| Project | Before | After |
|---|---|---|
| WireMock.Net.Minimal | References WebSockets ❌ | No WebSocket ref ✅ |
| WireMock.Net | References WebSockets ✅ | References WebSockets ✅ |
| WireMock.Net.WebSockets | N/A | Only refs Shared ✅ |
🎯 Pattern Consistency
Comparison with Existing Optional Features
| Feature | Pattern | Location | Dependency |
|---|---|---|---|
| ProtoBuf | Extension methods | WireMock.Net.ProtoBuf | Optional |
| GraphQL | Extension methods | WireMock.Net.GraphQL | Optional |
| MimePart | Extension methods | WireMock.Net.MimePart | Optional |
| WebSockets | Extension methods | WireMock.Net.WebSockets | Optional ✅ |
🚀 How It Works
1. User installs WireMock.Net
- Gets HTTP/REST mocking
- WebSocket support included but optional
2. User uses WebSocket extensions
using WireMock.WebSockets; // Brings in extension methods
// Extension methods now available
server.Given(Request.Create().WithWebSocketPath("/ws"))
3. Behind the scenes
- Extension methods call WebSocket matchers
- WebSocket configuration stored separately
- Middleware can check for WebSocket config
- Handler invoked if WebSocket is configured
📊 Code Organization
Extension Method Storage
Response builder uses ConditionalWeakTable<IResponseBuilder, WebSocketConfiguration> to store WebSocket settings without modifying the original Response class:
private static readonly ConditionalWeakTable<IResponseBuilder, WebSocketConfiguration> WebSocketConfigs = new();
internal class WebSocketConfiguration
{
public Func<WebSocketHandlerContext, Task>? Handler { get; set; }
public Func<WebSocketMessage, Task<WebSocketMessage?>>? MessageHandler { get; set; }
public TimeSpan? KeepAliveInterval { get; set; }
public TimeSpan? Timeout { get; set; }
}
This allows:
- Zero modifications to Response class ✅
- Clean separation of concerns ✅
- No performance impact on non-WebSocket code ✅
- Thread-safe configuration storage ✅
✅ Compilation Status
- Errors: 0
- Warnings: 0
- Dependencies: Only WireMock.Net.Shared
- External Packages: None
- Pattern: Matches ProtoBuf exactly ✅
🎓 Summary
The WebSocket implementation now:
- ✅ Follows the ProtoBuf extension method pattern
- ✅ Has zero external dependencies
- ✅ Is completely optional (no WireMock.Net.Minimal coupling)
- ✅ Uses ConditionalWeakTable for configuration storage
- ✅ Provides a clean, discoverable API
- ✅ Maintains full backward compatibility
- ✅ Compiles without errors or warnings
The implementation is now properly architected, following WireMock.Net's established patterns for optional features!