Add SignalR to Net6 Web Api and connect from Winforms application (.net fx4.8) - hub not found - winforms

I'm trying to add signalr to the webapi, I create the CucinaHub class:
public class CucinaHub : Hub
{
static ConcurrentDictionary<string, string> _users = new ConcurrentDictionary<string, string>();
#region Client Methods
public void SetUserName(string userName)
{
_users[Context.ConnectionId] = userName;
}
#endregion Client Methods
}
and configure SignalR:
services.AddSignalR();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<CucinaHub>("/cucinahub");
});
Then in Windows form application I use this code to connect with the hub:
_signalRConnection = new HubConnection($"{Properties.Settings.Default.URL}/api", true);
_hubProxy = _signalRConnection.CreateHubProxy("/cucinahub");
_hubProxy.On("GetValvole", async () => await GetValvole());
try
{
//Connect to the server
await _signalRConnection.Start();
}
catch (Exception ex)
{
Log.Error(ex.ToString());
}
I get always 404 response code:
Hosting environment: Development
Content root path: D:\SwDev\PigsutffHTML\Server\Common\Common.WebApiCore
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET http://localhost:5000/api/signalr/negotiate?clientProtocol=2.1&connectionData=[%7B%22Name%22:%22/cucinahub%22%7D] - -
warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3]
Failed to determine the https port for redirect.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 GET http://localhost:5000/api/signalr/negotiate?clientProtocol=2.1&connectionData=[%7B%22Name%22:%22/cucinahub%22%7D] - - - 404 0 - 122.4090ms
Where is the error?
Thank you

The key is using the package Microsoft.AspNetCore.SignalR.Client on the WinForm project as client. It is available on .NETFramework. You can see from the picture here:
For testing purpose, I created one webapi project and another winform project. Winform project has been installed the package. Then you can follow the MS document to set up the hubs in webapi project.
Then start the webapi project first to see which port it's running on. Now you can follow the MS Document for setting Clients to set client connect to hub.
For testing purpose, I created two button functions to connect hub and send message to hub, here is the code of connect and send message function button:
private void btnconnect_Click(object sender, EventArgs e)
{
try
{
connection.StartAsync().Wait();
}
catch (Exception ex)
{
//...
}
}
private void btnsend_Click(object sender, EventArgs e)
{
try
{
connection.InvokeAsync("SendMessage",
"winformclient", "hello world").Wait();
}
catch (Exception ex)
{
//...
}
}
And here is the code for setting up client connecting(Remember using your own port here):
HubConnection connection;
public Form1()
{
InitializeComponent();
connection = new HubConnectionBuilder()
.WithUrl("https://localhost:7090/ChatHub")
.Build();
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await connection.StartAsync();
};
}
Now start the WinForm Application, click the send button and you can get the result here:
You can see from the screenshot, the hub has already got the message from WinForm application.

Short answer - You cannot mix the .NET 4.x Framework and the .NET Core server/clients. Neither one can talk to the other. You have to upgrade your client. More info in this SO answer.

Related

How to properly configure websocket with springboot and reactjs?

I can establish a websocket connection with my springboot server but I can't access the endpoint from #MessageMapping when I'm trying to send a message. Here are my configurations:
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/simulator")
.setAllowedOrigins("http://myiphere:3000")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/endpoint");
registry.setApplicationDestinationPrefixes("/app");
}
And a simple controller :
#RestController
#RequestMapping("/api")
public class MyController {
#MessageMapping("/hello/")
#SendTo("/endpoint/greeting")
public Greeting getCurrentLocation() {
System.out.println("hello here");
return GenericBuilder.of(Greeting::new)
.with(Greeting::setContent, "hello from server")
.build();
}
}
I'm using the socketjs-client library in ReactJS by following this tutorial :
import SockJS from "sockjs-client";
import Stomp from "stompjs";
let stompClient;
const connect = () => {
const socket = new SockJS("http://myiphere:8081/simulator");
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log("Connected " + frame);
stompClient.subscribe("http://myiphere:8081/endpoint/greeting", function (greeting) {
console.log("hi" + JSON.parse(greeting.body).content);
});
});
};
const sendSomething = () => {
stompClient.send("http://myiphere:8081/app/hello/", {});
};
And some buttons with onClick events bound to the methods above. The connection is working, I'm getting "connected" messages in browser console but when I'm trying to click the button with sendSomething() I'm not getting anything in the browser's console nor server's console.
Solved.
The problem was the absolute url path in the send() method.
P.S.: And I've been looking for an answer for this problem on many sites and found out that there is no need to use absolute path for subscribe() url.
P.P.S.: In case that someone else have these problems, look for extra / too. You have to be careful when you're setting the url. The pattern from JS should match the one from SpringBoot.

SignalR Hub not receiving user from client WPF (.netcore 3.1)

I have a WPF client which connects successfully to a Hub, but I cannot pass the user of the client to the Hub.
My connection.User?.Identity?.Name in my class implementing from IUserIdProvider returns null.
For my WPF client I use this to connect against the Hub:
_connection = new HubConnectionBuilder()
.WithUrl(viewModel.Endpoint, opts =>
{
opts.Credentials = new NetworkCredential("user", "password", "domain");
opts.UseDefaultCredentials = true;
})
.Build();
I have then the following provider registered as singleton:
public class NameUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
return connection.User?.Identity?.Name;
}
}
As I mentioned above, the connection.User?.Identity?.Name; is returning null.
I don't know what else I can do to pass the user name from my client (WPF) to my Hub.
By the way, my Startup.cs looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddLogging();
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
services.AddSignalR(hubOptions =>
{
hubOptions.EnableDetailedErrors = true;
});
services.AddScoped<IBuildsService, BuildsService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<SyncCodeHub>("/signalr");
});
}
Any help would be much appreciated.
EDIT:
I update the code with:
services.AddAuthentication(IISDefaults.AuthenticationScheme);
But the problem continues, the identity user (IUserIdProvider) is returning null when called from the WPF client. I'm running the API locally with IISExpress.
EDIT:
From Microsoft docs:
Windows Authentication is only supported by the browser client when using Microsoft Internet Explorer or Microsoft Edge.
So I'm wondering if this is even possible with an Desktop as a client. I assume it should work, so I'm wondering if I'm still missing a point or if this is a bug related to the Version of SignalR I#m using (3.1.3)
You need to configure your ASP.NET Core app to use Windows authentication by calling AddAuthentication in the ConfigureServices method of the Startup class:
services.AddAuthentication(IISDefaults.AuthenticationScheme);
You should also edit your launchSettings.json file according to the docs:
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": false,
"iisExpress": {
"applicationUrl": "http://localhost:52171/",
"sslPort": 44308
}
}

Microsoft Botframework V4 Virtual Assistant Azure AD Authentication

I have downloaded, configured and deployed the Microsoft Virtual Assistant open source project from GitHub here: https://github.com/Microsoft/AI
I want to start with the calendar skill and have configured everything.
When I request my current calendar entries, the authentication prompt is shown in the botframework emulator and I am able to authenticate with my Azure AD Account.
After that, there is silence...
In SummaryDialog.cs in the CalendarSkill there is a definition for a WaterfallStep like this:
var showSummary = new WaterfallStep[]
{
GetAuthToken,
AfterGetAuthToken,
ShowEventsSummary,
CallReadEventDialog,
AskForShowOverview,
AfterAskForShowOverview
};
The step GetAuthToken is executed, but then execution stops. AfterGetAuthToken is not called at all.
This is the GetAuthToken function inside the project:
protected async Task<DialogTurnResult> GetAuthToken(WaterfallStepContext sc, CancellationToken cancellationToken)
{
try
{
var skillOptions = (CalendarSkillDialogOptions)sc.Options;
// If in Skill mode we ask the calling Bot for the token
if (skillOptions != null && skillOptions.SkillMode)
{
// We trigger a Token Request from the Parent Bot by sending a "TokenRequest" event back and then waiting for a "TokenResponse"
// TODO Error handling - if we get a new activity that isn't an event
var response = sc.Context.Activity.CreateReply();
response.Type = ActivityTypes.Event;
response.Name = "tokens/request";
// Send the tokens/request Event
await sc.Context.SendActivityAsync(response);
// Wait for the tokens/response event
return await sc.PromptAsync(SkillModeAuth, new PromptOptions());
}
else
{
return await sc.PromptAsync(nameof(MultiProviderAuthDialog), new PromptOptions());
}
}
catch (SkillException ex)
{
await HandleDialogExceptions(sc, ex);
return new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs);
}
catch (Exception ex)
{
await HandleDialogExceptions(sc, ex);
return new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs);
}
}
Am I doing anything wrong in the code or is there anything missing in my configuration?
I found out, if ngrok is not on the PC and configured, the virtual Assistatn does not work.

How do I secure my Google Cloud Endpoints APIs with Firebase token verification?

My setup:
Java backend hosted on Google App Engine containing APIs that were created using Google Cloud Endpoints
Mobile client applications containing generated client libraries for the endpoints mentioned above. Also integrated with Firebase for authentication and the database.
My intention is that a user of the mobile client applications will be able to log in to the mobile app using Firebase authentication, then connect to any of the backend APIs, which in turn will do some processing and then read or write data to/from the Firebase database.
To secure the APIs on the server, I think I'll have to use the built-in verifyIdToken() method of the Firebase Server SDK to (see Verifying ID Tokens on Firebase) to decode a user's ID token passed from the client application. As verifyIdToken() runs asynchronously, how would I integrate it with an API method in GAE? I have something similar to the following so far:
#ApiMethod(name = "processAndSaveToDB", httpMethod = "post")
public Response processAndSaveToDB(#Named("token") String token) {
Response response = new Response();
// Check if the user is authenticated first
FirebaseAuth.getInstance().verifyIdToken(idToken)
.addOnSuccessListener(new OnSuccessListener() {
#Override
public void onSuccess(FirebaseToken decodedToken) {
String uid = decodedToken.getUid();
// do bulk of processAndSaveToDB() method
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(Exception e) {
// throw unauthorized exception
});
return response;
}
As this authentication task is running asynchronously in task queue, you can wait until that task is ended and continue in synchronous way, optionally you can add listeners onSuccess, onFailure and onComplete.
Task<FirebaseToken> authTask = FirebaseAuth.getInstance().verifyIdToken(idToken)
.addOnSuccessListener(new OnSuccessListener() {
#Override
public void onSuccess(Object tr) {//do smtg }
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(Exception excptn) {//do smtg }
}).addOnCompleteListener(new OnCompleteListener() {
#Override
public void onComplete(Task task) {//do smtg }
});
try {
Tasks.await(authTask);
} catch(ExecutionException | InterruptedException e ){
//handle error
}
FirebaseToken decodedToken = authTask.getResult();

Host WebSocket Server in ASP.NET WebAPI on Console Application

I have built an ASP.NET WebAPI which is hosted on a console application. There are some web apis I had created and worked well. Then I tried to implement web socket service on it. The server side code was like below
[RoutePrefix("api/notification")]
public class NotificationController : ApiController
{
[HttpGet]
[Route("")]
public HttpResponseMessage Get()
{
if (HttpContext.Current.IsWebSocketRequest)
{
HttpContext.Current.AcceptWebSocketRequest(new NotificationWebSocketHandler());
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
public class NotificationWebSocketHandler : WebSocketHandler
{
private static WebSocketCollection _clients;
static NotificationWebSocketHandler()
{
_clients = new WebSocketCollection();
Task.Factory.StartNew(() =>
{
while (true)
{
Task.Delay(TimeSpan.FromSeconds(5));
if (_clients.Count > 0)
{
_clients.Broadcast(Guid.NewGuid().ToString());
}
}
});
}
public override void OnOpen()
{
_clients.Add(this);
Console.WriteLine("Web socket client opened, client count = {0}", _clients.Count);
}
public override void OnClose()
{
_clients.Remove(this);
Console.WriteLine("Web socket client closed, client count = {0}", _clients.Count);
}
}
}
But when I opened the client page (which was built on AngularJS) I got the error message said WebSocket connection to 'ws://10.222.115.220:8080/api/notification/' failed: Error during WebSocket handshake: Unexpected response code: 500
My client side code was
app.shared.controllers.controller('DashboardCtrl', function ($q, $scope) {
var ws = new WebSocket("ws://10.222.115.220:8080/api/notification/");
ws.onopen = function () {
console.log('web socket opened');
}
ws.onmessage = function (message) {
$scope.seed = message;
};
$scope.seed = '(empty)';
});
When I attached debug and added a breakpoint at the entry of my Get function, and I found the error 500 was raised without this breakpoint hit. I think this means the error was generated by WebAPI internally.
I'm not sure if anything wrong in my code, or WebSocket feature doesn't support console application host.
you are using the Web API in "Self Host"-mode? (hosted in the console)
There you have the problem that the object HttpContext.Current is "null".
This is a problem of self-hosting. HttpContext.Current is set while using the IIS (web-hosting). This property is not available during self-hosting. I think this could be your problem. (A null-ref-exception is thrown in your web-api-application and returns 500 - internal server error).

Resources