Azure Signal R Service with Angular 13 - c#

I've been trying to setup an integration between an Angular 13 client-side app and a .NET Framework 4.8 API with an Azure Signal R Service. After much trial and error this week I have finally got my api to build without CORS errors and I can make a successful negotiate call using postman. I am using the #aspnet/signalr client-side package since that is the only signal R package that support Azure Signal R Services.
Below is the angular code I am using to make a connection to my signal R hub named DashboardHub
import { Injectable } from '#angular/core';
import * as signalR from '#aspnet/signalr';
#Injectable()
export class SignalrService {
public initializeSignalRConnection(): void {
let connection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:58556/signalr/dashboardhub")
.configureLogging(signalR.LogLevel.Debug)
.build();
connection.start()
.then(() => {
console.log("connection.start");
//connection.invoke("send", "Hello")
})
.catch((error) => {
console.log("connection start error", error);
});
connection.on("send", data => {
console.log(data);
});
}
}
The problem I am having is within the connection.start which returns an error Cannot read properties of undefined (reading 'length'). Screenshot of that browser error below.
API Signal R mapping for context
And here is the DashboardHub for reference

Related

.NET WPF equivalent of laravel-echo

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");
}
}

Azure SignalR Blazor app not receiving messages

I'm looking at incorporating Azure SignalR functionality into my .net core Blazor web application. To this end i've been following this tutorial - Azure Signalr Serverless. This is working fine - i have a project running the Azure functions app and can start up two browsers and have a chat session. What i'm trying to do is add the ability to receive these message notifications from the Azure signalR hub that's been configured into my Blazor app. I've added the following code in Index.razor.cs that mimics the javascript code in the example client:
public class IndexComponent : ComponentBase
{
private HubConnection _connection;
public string Message;
protected override Task OnInitializedAsync()
{
_connection = new HubConnectionBuilder()
.WithUrl("http://localhost:7071/api")
.Build();
_connection.On<string, string>("ReceiveMessage", (user, message) =>
{
Message = $"Got message {message} from user {user}";
this.StateHasChanged();
});
_connection.StartAsync();
return base.OnInitializedAsync();
}
}
The example javascript code btw is:
const connection = new signalR.HubConnectionBuilder()
.withUrl(`${apiBaseUrl}/api`)
.configureLogging(signalR.LogLevel.Information)
.build();
connection.on('newMessage', newMessage);
connection.onclose(() => console.log('disconnected'));
console.log('connecting...');
connection.start()
.then(() => data.ready = true)
.catch(console.error);
So the problem is that my Blazor app never receives any message notifications sent from the javascript chat clients (so the _connection.On handler is never hit). What am i missing in my Blazor code ?
Ok so this is what i needed to do to get it to work in my Blazor app:
_connection.On<object>("newMessage", update =>
{
Console.WriteLine(update);
//Message = update;
});
I needed to subscribe to the 'newMessage' target (since that's the JS is sending on) and also the type that's being posted isn't a string but a JObject type which i would need to deserialize to the correct type.

SignalR - Client connection closes straight after starting - InvalidOperationException: Sequence contains no elements

I have a a SignalR client which seems to close straight after starting, the error message i get is:
"The server closed the connection with the following error: Connection closed with an error. InvalidOperationException: Sequence contains no elements"
The SignalR client is being used in a ASP.Net Core Web API project (within an API controller).
The Nuget package i am using is called Microsoft.AspNetCore.SignalR.Client (v 1.1.0)
My code looks like this:
try
{
//SEND MESSAGE TO HUB
var connection = new HubConnectionBuilder()
.WithUrl("https://sample.azurewebsites.net/ChatHub")
.Build();
connection.Closed += async (error) =>
{
//log error - this is getting called straight after StartAsync
};
await connection.StartAsync();
await connection.InvokeAsync("SendToATeam", "x", "y");
await connection.StopAsync();
}
catch (Exception ex)
{
//log error
}
On your server you can turn on detailed errors via:
services.AddSignalR(o =>
{
o.EnableDetailedErrors = true;
})
This will then give you a more detailed error message on the client

Exchanging NServiceBus messages with native implementation of Azure Service Bus

Consuming a message published with NServiceBus, using IQueueClient/Functions-ServiceBusTrigger (Microsoft.Azure.ServiceBus)
I'm working in a WebJob using .NET Core and Microsoft.Azure.ServiceBus to consume a message that has been published by a separate service using NServiceBus. My initial approach with this WebJob was to use a class Functions.cs with a method ProcessMessage that uses the attribute ServiceBusTrigger
Below is an example of how my Function.cs looks like:
public class Functions
{
public Task ProcessAuditLogMessage([ServiceBusTrigger("MyQueue")]
Message message)
{
var messageBody = Encoding.UTF8.GetString(message.Body);
var auditLogMessage = JsonConvert
.DeserializeObject<MyMessage>(messageBody);
_logger.Information("Hello World");
return Task.CompletedTask;
}
}
In Program.cs, I have:
class Program
{
static async Task Main()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddWebJobs(o => o.AllowPartialHostStartup = true);
var builder = new HostBuilder()
.UseServiceProviderFactory(
new AutofacServiceProviderFactory(serviceCollection))
.ConfigureContainer<ContainerBuilder>(b =>
{
BuildAutofacContainer();
})
.ConfigureWebJobs(b =>
{
b.AddServiceBus(o =>
{
o.ConnectionString = configProvider.AuditLogServiceBus;
});
});
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
private static IContainer BuildAutofacContainer(IServiceColletion
serviceCollection)
{
...
}
}
I was expecting that this logic would consume the messages published in the Queue but so far the messages get moved to the Dead-letter count with a DeadLetterReason: MaxDeliveryCountExceeded and the error description Message could not be consumed after 10 delivery attempts which gives me the indication that at least there is an attempt to get these messages delivered to my consumer.
Unfortunately that's all I have in terms of error messages/logs (I'm in the process to set up some logs from my Azure Dashboard and see if I can get more detailed logs)
Has anyone come across the scenario o having to consume messages, that have been published with NServiceBus, using Microsoft.Azure.ServiceBus instead of NServiceBus (on the consumer side). Maybe I'm missing something...

SignalR generates lots of 403 errors

I've created small module for website, which uses SignalR to detect if user needs to refresh the browser. Locally it works, but when code went into production, thousands of errors are produced:
request: (lots of pages from website)
referer: https://(website)/signalr/abort
error message: 403: Http Error 403
Server side: (mainProject/Hubs directory):
public class AppVersionNotifierHub : Hub<IAppVersionNotifierHub>
{
public void CheckAppVersion(string version)
{
// if client has diffrent version, invoke callback
if (Global.Version != version)
{
Clients.Caller.NewAppVersion(Global.Version);
}
}
}
Javascript (type script):
this.subscribeVersionChecker = () => {
var hub = (<any>$.connection).appVersionNotifierHub;
hub.client.newAppVersion = (version: string) => {
.. some logic
}
$.connection.hub.start(() => {
hub.server.checkAppVersion(customerVersion.text());
});
$.connection.hub.reconnected(() => {
setTimeout(() => {
hub.server.checkAppVersion(customerVersion.text());
}, 5000); // Restart connection after 5 seconds.
});
$.connection.hub.disconnected(() => {
setTimeout(() => {
$.connection.hub.start();
}, 10000); // Restart connection after 10 seconds.
});
};
Any ideas why some clients generates errors ?
Site is hosted on azure
To use bundles, I've copied dynamically generated signalr.js file into Scripts\signalrhub.js file
I have discovered the problem now.
Problem was with authentication, I've read this article: http://www.bitwisejourneys.com/signalr-authenticating-even-when-you-dont-think-it-is/ and after some thinking I was able to reproduce the problem.
Open site in one tab and log in
Open site in the other tab and log out
Restart the connection on server
Tab 1 is sending the same authentication token as Tab 2. Server denies Tab 1 but responds to Tab 2
My solution: when connection is lost, I'm not trying to reconnect, but to stop and restart the connection (note I've changed the event from Reconnected to Reconnecting !)
$.connection.hub.reconnecting(() => {
$.connection.hub.stop();
setTimeout(() => {
$.connection.hub.start();
}, 5000); // Restart connection after 5 seconds.
});

Categories

Resources