I am trying to add a SignalR client to specific SignalR group on an azure functions server (isolated-process). The server side code that I am trying to invoke is the following:
[Function("SendToGroup")]
[SignalROutput(HubName = "serverless", ConnectionStringSetting = "AzureSignalRConnectionString")]
public static SignalRMessageAction SendToGroup([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
using var bodyReader = new StreamReader(req.Body);
return new SignalRMessageAction("newMessage")
{
Arguments = new[] { bodyReader.ReadToEnd() },
GroupName = "groupToSend"
};
}
I have tried invoking the above from the client side in C# with the following code to no avail:
HubConnection _connection = new HubConnectionBuilder().WithUrl("http://localhost:7071/api").Build();
Dispatcher.Dispatch(async () => await _connection.StartAsync());
// This does not work
await HubConnectionExtensions.InvokeAsync(_connection, "SendToGroup");
In my searches, I have not found a single C# client code sample that shows how to connect to and remove oneself from signalR hub groups. I would really appreciate being pointed in the right direction.
Related
Ivé this code...
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "v1/Sala")]
HttpRequest req, [SignalR(HubName = "{query.HubName}")]
IAsyncCollector<SignalRMessage> signalRMessages) {
await signalRMessages.AddAsync(
new SignalRMessage {
Target = "newMessage",
Arguments = new[] { "Hello" }
});
}
And this code works good and I am happy. But I've a need, in partiular on my EventGridTrigger...
As you could noticed on the code above, the hubname is dinamic and an EventGridTrigger is a sort of special kind of endpoint (Your client app will not call and consume it...SignalR will instead).
But I am capable to identify the hubname on my EventGridTrigger...I can do this:
SignalRDataEvent data =
JsonConvert.DeserializeObject<SignalRDataEvent(eventGridEvent.Data.ToString());
string hubname = data.hubname;
But now...I need to send a signalR message using my variable hubname. Since I can't put [SignalR(HubName = "{query.HubName}")] IAsyncCollector signalRMessages) on my EventGridTrigger, I need to create the object SignalR, probably pass credentials, hubname, etc and then send a message. I can't find any sample for this - At least no samples that can work in serverless c# azure functions. Can someone help me wikth this ?
Try the following:
[SignalR(HubName = "{data.hubName}")]
I am building a simple restaurant management system in WPF. I have my backend in Laravel. I needed to setup a web socket to get real-time notifications on WPF app when a customer places an order from mobile app. I configured the web socket in Laravel using beyondcode/laravel-websockets. For ease, I tested the web socket on client side using laravel-echo with Vue. Everything works well there but I couldn't find any solution to replicate laravel-echo in C#.
Here is the code I am using in Vue.js with laravel-echo:
import Echo from "laravel-echo";
import Pusher from "pusher-js";
window.Pusher = Pusher;
const token = "1|CSaob3KZhU5UHiocBjPgzpazbceUKTLRLJO0ZIV0"
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'laravel_rdb',
wsHost: '127.0.0.1',
authEndpoint: 'http://localhost/may-app/public/broadcasting/auth',
encrypted: false,
forceTLS: false,
wsPort: 6001,
wssPort: 6001,
disableStats: true,
enabledTransports: ['ws', 'wss'],
auth : {
headers : {
Authorization: "Bearer " + token,
Accept: "application/json",
}
},
})
window.Echo.private('customer-order')
.listen('OrderPlaced', (e) => {
console.log(e)
})
I found SocketIOClient is used to implement web socket functionality in .NET. I tried to use a solution I found here but it didn't work for me. Also, I didn't find any way to set up my authentication URL in this package. I read socket.io documentation for anything related to authentication but I couldn't find any.
How do I implement equivalent functionality in C# .NET as in laravel-echo?
There is probably no client like laravel-echo for .NET. However, you will be able to connect to your sockets using pusher client: pusher/pusher-websocket-dotnet and this is probably the highest level of compatibility you can reach. But you will need to parse your messages and subscribe to the channels by yourself, there will be no sweet wrapping like in laravel-echo =(
I was able to implement a solution using the package mentioned by PunyFlash in the answers. The NuGet package is available here and here is the GitHub repo.
My solution might be useful for someone in the future so, my equivalent code for the laravel-echo code above, in .NET is:
internal class OrderSocket
{
public static async void Connect()
{
try
{
//Setting authentication
var authorizer = new CustomAuthorizer("http://localhost/may-app/public/broadcasting/auth")
{
AuthenticationHeader = new System.Net.Http.Headers.AuthenticationHeaderValue("Authorization", "Bearer " + "1|CSaob3KZhU5UHiocBjPgzpazbceUKTLRLJO0ZIV0"),
};
//Creating pusher object with authentication
Pusher pusher = new Pusher("laravel_rdb", new PusherOptions
{
Authorizer = authorizer,
Host = "127.0.0.1:6001",
});
//Connecting to web socket
await pusher.ConnectAsync().ConfigureAwait(false);
//Subscribing to channel
Channel channel = await pusher.SubscribeAsync("private-customer-order").ConfigureAwait(false);
if (channel.IsSubscribed)
{
//Binding to an event
channel.Bind("App\\Events\\OrderPlaced", (PusherEvent eventResponse) =>
{
// Deserialize json if server returns json values
Debug.WriteLine(eventResponse.Data);
});
}
}
catch (Exception)
{
Debug.WriteLine("An exception occurred.");
}
}
}
//HttpAuthorizer child class to set default headers
internal class CustomAuthorizer : HttpAuthorizer
{
public CustomAuthorizer(string authEndpoint) : base(authEndpoint) { }
public override void PreAuthorize(HttpClient httpClient)
{
base.PreAuthorize(httpClient);
httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
}
}
I’m developing a app that used CosmosDB to store data and then when anyone updates the data i want the clients to be updated.
For this i have decided to use the changefeed and then Azure Functions and Azure SignalR.
I have set up 2 functions.
A negotiate function (This one works and the clients connect correctly to the SignalR server)
And a OnDocumentsChanged function, and my problem is getting the function to actually sending the message, when something is changed.
I have the following function:
[FunctionName("OnDocumentsChanged")]
public static async Task Run(
[CosmosDBTrigger(
databaseName: "NewOrder",
collectionName: "NewOrder",
CreateLeaseCollectionIfNotExists = true,
ConnectionStringSetting = "myserver_DOCUMENTDB",
LeaseCollectionName = "leases")]
IReadOnlyList<Document> updatedNewOrder,
[SignalR(ConnectionStringSetting = "AzureSignalRConnectionString", HubName = "NewOrder")] IAsyncCollector<SignalRMessage> signalRMessages,
ILogger log)
{
if (updatedNewOrder != null && updatedNewOrder.Count > 0)
{
foreach (var Orders in updatedNewOrder)
{
await signalRMessages.AddAsync(new SignalRMessage
{
Target = "NewOrderUpdated",
Arguments = new[] { Orders.Id }
});
}
}
}
I can see that it is correctly triggered when a change is made to the database, but no messages are send.
I guess I’m missing a out part that actually send the SignalRMessages I’m just not sure how to implement.
Thanks.
I have a WebSite integrated with SignalR. It functions well, and it has a button which sends popup notification to all clients who are online. It works well when I click on the button.
My API is in another project but in the same Solution. I want to send the above notification by calling from the API side. Basically, a mobile app will send a request to API and then API will send a notification to all online web clients.
Below code runs and not gives the notification nor any error.
Is this fundamentally correct? Appreciate your help
API code (at WebAPI project)
[HttpGet]
public IEnumerable<string> WatchMe(int record_id)
{
GMapChatHub sendmsg = new GMapChatHub();
sendmsg.sendHelpMessage(record_id.ToString());
return "Done";
}
C# code (at Web project)
namespace GMapChat
{
public class GMapChatHub : Hub
{
public void sendHelpMessage(string token)
{
var context = GlobalHost.ConnectionManager.GetHubContext<GMapChatHub>();
context.Clients.All.helpMessageReceived(token, "Test help message");
}
}
}
Home.aspx file (at Web project)
var chat = $.connection.gMapChatHub;
$(document).ready(function () {
chat.client.helpMessageReceived = function (token,msg) {
console.log("helpMessageReceived: " + msg);
$('#helpMessageBody').html(msg)
$('#helpModal').modal('toggle');
};
}
You can not call that hub directly. Firs you need to install the .net client for SignalR from nuget. Then you need to initialize it like this :
[HttpGet]
public IEnumerable<string> WatchMe(int record_id)
{
using (var hubConnection = new HubConnection("your local host address"))
{
IHubProxy proxy= hubConnection.CreateHubProxy("GMapChatHub");
await hubConnection.Start();
proxy.Invoke("sendHelpMessage",record_id.ToString()); // invoke server method
}
// return sth. IEnumerable<string>
}
And opening a new connection per request may not be good idea you may make it per session (if you use) or static or time fashioned.
I have a ASP.NET Web Application with a simple HTML page and some JavaScript to communicate via SignalR. That works fine.
Now I'm trying to call a method on the Hub from another project (in the same solution) and by using the .NET Signalr Client Api:
var connection = new HubConnection("http://localhost:32986/");
var hub = connection.CreateHubProxy("MessageHub");
connection.Start();
hub.Invoke("SendMessage", "", "");
The last line causes InvalidOperationException: The connection has not been established. But I am able to connect to the hub from my JavaScript code.
How can I connect to the Hub by using C# code?
UPDATE
The moment after writing this post, I tried to add .Wait() and it worked!
So this will do:
var connection = new HubConnection("http://localhost:32986/");
var hub = connection.CreateHubProxy("MessageHub");
connection.Start().Wait();
hub.Invoke("SendMessage", "", "");
HubConnection.Start returns a Task that needs to complete before you can invoke a method.
The two ways to do this are to use await if you are in an async method, or to use Task.Wait() if you are in a non-async method:
public async Task StartConnection()
{
var connection = new HubConnection("http://localhost:32986/");
var hub = connection.CreateHubProxy("MessageHub");
await connection.Start();
await hub.Invoke("SendMessage", "", "");
// ...
}
// or
public void StartConnection()
{
var connection = new HubConnection("http://localhost:32986/");
var hub = connection.CreateHubProxy("MessageHub");
connection.Start().Wait();
hub.Invoke("SendMessage", "", "").Wait();
// ...
}
The "How to establish a connection" section of the ASP.NET SignalR Hubs API Guide for the .NET client. goes into even more detail.