Adds more confs for handlers and try to fix error with swagger when using attribute

This commit is contained in:
Dmitri Shimanski
2025-04-01 18:54:55 +03:00
parent 54779bb793
commit 59ddda6077
8 changed files with 68 additions and 28 deletions

View File

@@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Mvc;
namespace Examples.Controllers;
[ApiController]
[Route("/api/v1/test")]
public class TestDefaultController : ControllerBase
{
[HttpGet]
public async Task<IActionResult> TestController([FromBody] string TextTest)
{
return Ok("Test");
}
}

View File

@@ -1,7 +1,6 @@
using System.Net.WebSockets; using System.Net.WebSockets;
using yawaflua.WebSockets; using yawaflua.WebSockets;
using yawaflua.WebSockets.Core; using yawaflua.WebSockets.Core;
using yawaflua.WebSockets.Models.Interfaces;
namespace Examples; namespace Examples;
@@ -22,18 +21,20 @@ class Program
internal class Startup internal class Startup
{ {
public void ConfigureServices(IServiceCollection services) public static void ConfigureServices(IServiceCollection services)
{ {
services.SettingUpWebSockets(); services.SettingUpWebSockets();
services.AddRouting(); services.AddRouting();
services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddHttpLogging(); services.AddHttpLogging();
services.AddSingleton<TestWebSocketServer>(); services.AddSingleton<TestWebSocketServer>();
services.AddSingleton<ChatController>(); services.AddSingleton<ChatController>();
services.AddSingleton(new WebSocketConfig() services.AddSingleton(new WebSocketConfig()
{ {
OnOpenHandler = async (socket, context) => OnOpenHandler = async (socket, _) =>
{ {
if (socket.WebSocketManager!.GetAllClients().Count(k => if (socket.WebSocketManager.GetAllClients().Count(k =>
Equals(k.ConnectionInfo!.RemoteIpAddress!.MapToIPv4(), Equals(k.ConnectionInfo!.RemoteIpAddress!.MapToIPv4(),
socket.Client.ConnectionInfo!.RemoteIpAddress!.MapToIPv4())) >= 3) socket.Client.ConnectionInfo!.RemoteIpAddress!.MapToIPv4())) >= 3)
{ {
@@ -42,15 +43,16 @@ internal class Startup
Console.WriteLine($"{socket.Client.Id} has been connected to {socket.Client.Path}"); Console.WriteLine($"{socket.Client.Id} has been connected to {socket.Client.Path}");
} }
}); });
services.AddScoped<IConfiguration>(k => new ConfigurationBuilder() services.AddScoped<IConfiguration>(_ => new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true) .AddJsonFile("appsettings.json", true)
.Build()); .Build());
} }
public void Configure(IApplicationBuilder app) public static void Configure(IApplicationBuilder app)
{ {
app.ConnectWebSockets();
app.UseRouting(); app.UseRouting();
app.UseWebSocketWithSwagger();
app.UseHttpLogging(); app.UseHttpLogging();
app.UseWelcomePage(); app.UseWelcomePage();

View File

@@ -1,5 +1,7 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Routing;
namespace yawaflua.WebSockets.Attributes; namespace yawaflua.WebSockets.Attributes;
@@ -18,13 +20,17 @@ namespace yawaflua.WebSockets.Attributes;
/// When applied to methods, defines specific sub-routes (requires class-level base path). /// When applied to methods, defines specific sub-routes (requires class-level base path).
/// </remarks> /// </remarks>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class WebSocketAttribute : RouteAttribute [ApiExplorerSettings(IgnoreApi = true)]
public class WebSocketAttribute : RouteAttribute, IRouteTemplateProvider, IApiDescriptionVisibilityProvider
{ {
/// <summary> /// <summary>
/// Original route template specified in attribute /// Original route template specified in attribute
/// </summary> /// </summary>
public string Template { get; } public new string Template { get; }
public new int? Order { get; } = 0;
public new string? Name { get; }
/// <summary> /// <summary>
/// Creates WebSocket route definition /// Creates WebSocket route definition
/// </summary> /// </summary>
@@ -36,5 +42,8 @@ public class WebSocketAttribute : RouteAttribute
public WebSocketAttribute([RouteTemplate]string path) : base(path) public WebSocketAttribute([RouteTemplate]string path) : base(path)
{ {
Template = path; Template = path;
Name = path;
} }
public bool IgnoreApi => true;
} }

View File

@@ -6,5 +6,7 @@ namespace yawaflua.WebSockets.Core;
public class WebSocketConfig public class WebSocketConfig
{ {
public Func<IWebSocket, HttpContext, Task>? OnOpenHandler { get; set; } = null; public Func<IWebSocket, HttpContext, Task>? OnOpenHandler { get; set; } = null;
public Func<Exception, IWebSocket, HttpContext, Task>? OnErrorHandler { get; set; } = null;
public Func<Exception, HttpContext, Task>? OnConnectionErrorHandler { get; set; } = null;
} }

View File

@@ -140,21 +140,22 @@ public class WebSocketRouter
var path = context.Request.Path.Value; var path = context.Request.Path.Value;
if (Routes.TryGetValue(path, out var handler)) if (path != null && Routes.TryGetValue(path, out var handler))
{ {
var webSocket = await context.WebSockets.AcceptWebSocketAsync(); var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Task.Run(async () => await Task.Run(async () =>
{ {
IWebSocketClient client = null!;
var webSocketManager = new WebSocketManager();
try try
{ {
var webSocketManager = new WebSocketManager(); client = new WebSocketClient(context, webSocket, path);
var client = new WebSocketClient(context, webSocket, path);
Clients.Add(client); Clients.Add(client);
await Task.Run(async () => await Task.Run(async () =>
{ {
if (_webSocketConfig?.OnOpenHandler != null) if (_webSocketConfig?.OnOpenHandler != null)
await _webSocketConfig.OnOpenHandler(new WebSocket(webSocket, client, webSocketManager)!, context); await _webSocketConfig.OnOpenHandler(new WebSocket(webSocket, client, webSocketManager), context);
}, cts); }, cts);
var buffer = new byte[1024 * 4]; var buffer = new byte[1024 * 4];
@@ -179,6 +180,11 @@ public class WebSocketRouter
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(message:"Error with handling request: ",exception: ex); _logger.LogError(message:"Error with handling request: ",exception: ex);
await Task.Run(async () =>
{
if (_webSocketConfig?.OnErrorHandler != null)
await _webSocketConfig.OnErrorHandler(ex, new WebSocket(webSocket, client, webSocketManager), context);
}, cts);
} }
}, cts); }, cts);
@@ -186,11 +192,14 @@ public class WebSocketRouter
else else
{ {
context.Response.StatusCode = 404; context.Response.StatusCode = 404;
throw new KeyNotFoundException("Path not found");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, $"Error when handle request {context.Connection.Id}: "); _logger.LogError(ex, $"Error when handle request {context.Connection.Id}: ");
if (_webSocketConfig!.OnConnectionErrorHandler != null)
await _webSocketConfig.OnConnectionErrorHandler(ex, context);
} }
} }
} }

View File

@@ -9,14 +9,15 @@ internal class WebSocketClient : IWebSocketClient
private HttpContext HttpContext { get; set; } private HttpContext HttpContext { get; set; }
public Guid Id { get; } = Guid.NewGuid(); public Guid Id { get; } = Guid.NewGuid();
public string Path { get; } public string Path { get; }
public ConnectionInfo ConnectionInfo { get => HttpContext.Connection; } public ConnectionInfo ConnectionInfo => HttpContext.Connection;
public IDictionary<object, object> Items
public IDictionary<object, object>? Items
{ {
get => HttpContext.Items; get => HttpContext.Items;
set => HttpContext.Items = (value); set => HttpContext.Items = value;
} }
public HttpRequest HttpRequest { get => HttpContext.Request; } public HttpRequest HttpRequest => HttpContext.Request;
public WebSocket webSocket { get; } public WebSocket webSocket { get; }
internal WebSocketClient(HttpContext httpContext, WebSocket webSocket, string path) internal WebSocketClient(HttpContext httpContext, WebSocket webSocket, string path)

View File

@@ -8,7 +8,8 @@ namespace yawaflua.WebSockets;
public static class ServiceBindings public static class ServiceBindings
{ {
public static IServiceCollection SettingUpWebSockets(this IServiceCollection isc, WebSocketConfig? socketOptions = null) public static IServiceCollection SettingUpWebSockets(this IServiceCollection isc,
WebSocketConfig? socketOptions = null)
{ {
isc.AddSingleton<WebSocketRouter>(); isc.AddSingleton<WebSocketRouter>();
if (socketOptions != null) isc.AddSingleton(socketOptions); if (socketOptions != null) isc.AddSingleton(socketOptions);
@@ -19,10 +20,11 @@ public static class ServiceBindings
return isc; return isc;
} }
public static IApplicationBuilder ConnectWebSockets(this IApplicationBuilder iab) public static IApplicationBuilder UseWebSocketWithSwagger(this IApplicationBuilder app)
{ {
iab.UseWebSockets(); app.UseWebSockets();
iab.UseMiddleware<WebSocketMiddleware>(); app.UseMiddleware<WebSocketMiddleware>();
return iab;
return app;
} }
} }

View File

@@ -19,11 +19,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="(2023.3.0,)"/> <PackageReference Include="JetBrains.Annotations" Version=">= (2023.3.0,)"/>
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="(2.1.7,)" /> <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version=">= (2.1.7,)" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="(6.0.0,)" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version=">= (6.0.0,)" />
<PackageReference Include="System.Net.WebSockets" Version="(4.0.0,)"/> <PackageReference Include="System.Net.WebSockets" Version=">= (4.0.0,)"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="(2.2.0,)" PrivateAssets="all"> <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version=">= (2.2.2,)" PrivateAssets="all">
<Private>False</Private> <Private>False</Private>
</PackageReference> </PackageReference>
<None Include="..\README.md" Pack="true" PackagePath="\"/> <None Include="..\README.md" Pack="true" PackagePath="\"/>