问题
I am trying to implement live notifications in my web application. Only the users which are administrators in my web app should see the notifications.
So I setup the web socket in my startup.cs file which I think is not the right way
Startup.cs
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
ReceiveBufferSize = 4 * 1024
};
app.UseWebSockets(webSocketOptions);
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
and this is my Javascript
window.onload = () => {
if (/*User is Admin*/) {
//Establish Websocket
var socket = new WebSocket("wss:localhost:44301/ws");
console.log(socket.readyState);
socket.onerror = function (error) {
console.log('WebSocket Error: ' + error);
};
socket.onopen = function (event) {
console.log("Socket connection opened")
};
// Handle messages sent by the server.
socket.onmessage = function (event) {
var data = event.data;
console.log(data);
//Draw some beautiful HTML notification
};
}
}
now this all works, but I don't know how to send messages from my server controllers, something like this
[HttpGet]
public async Task<IActionResult> Foo(WebSocket webSocket)
{
//What I am trying to do is send message from the open web socket connection.
var buffer = new byte[1024 * 4];
buffer = Encoding.UTF8.GetBytes("Foo");
await webSocket.SendAsync(new ArraySegment<byte>(buffer),WebSocketMessageType.Text,true,CancellationToken.None);
return View()
}
I don't know how to approach this. What I wanna do is if the user is admin, open web socket and send some data from the other users actions, (which means writing messages from that opened web socket from some of my controllers)
回答1:
I had similar problem, with messaging system. One way to this in .NET CORE is to use SIGNALR
, so you must create for every user that you want to communicate connection. In your case every user have connection with admin. You should on back-end create class for storing this connections in hash table, and one class that implements method for sending. In Startup.cs i have only this:
services.AddSignalR();
services.AddSingleton<ChatHub>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Secret").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for our hub...
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/chat")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
ChatHub is class that Extends Hub
with method send and overrided methods OnConnectedAsync
and OnDisconnectedAsync
used to add or remove connection from hash. You make request on front when you want to send message.
If you want to this from your controller (if you use JWT this is not neccessary) you just need to inject ChatHub
in your controllers if you want and to call send with message.
In ChatHub
this is important line
await Clients.Client(connectionId).SendAsync("recievedMessage", messageToSend);
On front I used angular, so code is different, so please visit this link: c# SignalR.
回答2:
SignalR is your friend: https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-3.1
Your client would be something like this:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/MyHub")
.configureLogging(signalR.LogLevel.Information)
.build();
connection.start()
.then(function() {
});
connection.on("UpdateText",
(data) => {
if (!data) {
console.log("No Update Rerading Data");
return;
}
// Do whatever needs to be done
});
Your Hub:
public sealed class MyHub : Hub
{
public MyHub()
{ }
}
Then you would create a signalR provider that you could call from anywhere in your system. For example:
await _signalRProvider
.BroadcastAsync("MyHub", "UpdateText", "AdminsGroup", new { text = "Whatssaaaap!" })
.ConfigureAwait(false);
All what it would is use HttpClient to post json type payload to signalR endpoint for a specific group (or use if you prefer).
$"{_endpoint}/api/v1/hubs/{hubName}/groups/{groupName}"
Of course you would need to register hub etc. I skipped that part as it is all documented in the link above.
As an extra: If you are using Microsoft Azure this becomes even simpler as you can use SignalR serverless approach: https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-quickstart-azure-functions-javascript
来源:https://stackoverflow.com/questions/62756390/how-to-use-web-sockets-in-c-sharp-net-core-3-1