上一篇处理了超长消息的问题。我们的应用到目前为止还是单聊天室,这一篇就要处理的多聊天室的问题。
思路
- 第一个问题,怎么访问不同聊天室
这个可以采用路由参数
来解决。我把路由设计成这样/chat/{room}
。访问不同路径就代表进入不同聊天室。
- 第二个问题,怎么创建不同的聊天室
原来的聊天室是单例注入到主机的。因此,多聊天室就不能使用单例了。
我们可以使用一个聊天室工厂,根据路由参数来创建或返回已有的聊天室。而这个工厂需要保存已有的聊天室,所以聊天室工厂需要是单例的。
多聊天室情况下,每个聊天室有个名字还是有必要的
//WebSocketChatRoom.cspublic string roomName { get; set; }
实现
聊天室工厂
- 工厂类
//ChatRoomFactory.cs/// <summary>
/// 聊天室工厂
/// </summary>
public class ChatRoomFactory
{public ConcurrentDictionary<string, WebSocketChatRoom> _rooms;/// <summary>/// WebSocketChatRoom工厂/// </summary>/// <param name="createDelegate">在注册配置WebSocketChatRoom的构造委托</param>public ChatRoomFactory(Func<WebSocketChatRoom> createDelegate){CreateDelegate = createDelegate;_rooms = new ConcurrentDictionary<string, WebSocketChatRoom>();}private Func<WebSocketChatRoom> CreateDelegate { get; }public WebSocketChatRoom GetRoom(string path){ if (_rooms.TryGetValue(path ,out WebSocketChatRoom socketroom)) {return socketroom;}else{var newRoom = CreateDelegate();newRoom.roomName = path;this._rooms.TryAdd(path, newRoom);return newRoom;}}
}
- 注册工厂
//program.cs//添加聊天室服务(不需要了)
//builder.Services.AddSingleton<WebSocketChatRoom>();
//添加聊天室工厂
builder.Services.AddSingleton<ChatRoomFactory>((provider) =>
{return new ChatRoomFactory(() =>{return new WebSocketChatRoom();});
});
中间件改造
改造的点有两个
- 使用的服务从
WebSocketChatRoom
改为ChatRoomFactory
- 判断路由参数
//WebSocketChatRoomMiddleware.cspublic class WebSocketChatRoomMiddleware
{private readonly RequestDelegate _next;public WebSocketChatRoomMiddleware(RequestDelegate next, Func<ChatRoomFactory> handler){_next = next;Handler = handler;}public Func<ChatRoomFactory> Handler { get; }public async Task InvokeAsync(HttpContext context){await _next(context);WebSocket client = await context.WebSockets.AcceptWebSocketAsync();var room = Handler().GetRoom(context.Request.Path.Value.Trim('/')?.ToString() ?? "defaultRoom");await room.HandleContext(context, client);}
}
//WebSocketChatRoomMiddlewareExtensions.cspublic static class WebSocketChatRoomMiddlewareExtensions
{public static WebApplication UseWebSocketChatRoomMiddleware(this WebApplication builder){//建立websocket分支builder.MapWhen(c => c.WebSockets.IsWebSocketRequest, appbuilder =>{//授权appbuilder.Use(async (context, next) =>{if (context.User.Identity.IsAuthenticated)await next(context);elsecontext.Response.StatusCode = StatusCodes.Status403Forbidden;}).Map("/chat", roombranch =>{roombranch.UseMiddleware<WebSocketChatRoomMiddleware>(new Func<ChatRoomFactory>(() =>{return roombranch.ApplicationServices.GetRequiredService<ChatRoomFactory>();}));});});return builder;}
}
聊天室改造
聊天室基本不用改。这里为了明确不同聊天室,就在加入广播和退出广播中加上聊天室名字吧
//WebSocketChatRoom.cs//广播游客加入聊天室
CascadeMeaasge(null, $"{roomName}: {visitor.Name}加入聊天室");
...
//广播游客退出
CascadeMeaasge(visitor,$"{roomName}: {visitor.Name}退出聊天室");
测试
这里列出每个游客聊天截图,还是用的ApiPost来测的。