How to call function on javascript page from ASP.NET 5 WebAPI - javascript

Okay, maybe I'm doing this all wrong, but here goes:
I have a ASP.NET 5 WebAPI project running on dnxcore50. It has one very simple controller, which only implements the Get() method:
public class Data : Controller {
public JsonResult Get(){
return Json(/*some data from EF*/);
}
}
Additionally, I have index.html and myscripts.js files which host a very simple webpage.
When running the project, it opens on http://localhost:5000, I can browse to index.html and can call
$.getJSON('/api/Data', function(data){
/*do stuff*/
});
All of this works, on all OSX and Windows. So far so good!
Now, what I want: when the API is called (for example a POST by another client), it should send a message to the javascript to display an event.
What have I tried:
setting a flag in the API and polling from the javascript. This seems sub-optimal, but works.
playing around with socket.io, but I cannot find a dnxcore50 compatible way to communicate with it from the controller.
playing around with HTML5 WebSockets, but can't seem to establish communication from the controller.
How can I communicate from ASP.NET 5 (dnxcore50) to a javascript page?

OK after some research, I got it working using SignalR, as #Liam suggested.
This is how I got SignalR working with ASP.NET Core 1.0.
In Nuget.config in project root, add repository for SignalR:
<add key="AspNetVNext" value="https://www.myget.org/F/aspnetmaster/api/v3/index.json" />
In project.json add reference for SignalR:
"dependencies":{
...
"Microsoft.AspNet.SignalR.Server": "3.0.0-*"
}
In Startup.cs, register SignalR:
public void ConfigureServices(IServiceCollection services)
{
/* ...other services...*/
services.AddSignalR(options =>
{
options.Hubs.EnableDetailedErrors = true;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
/*...other configs...*/
app.UseSignalR();
}
Create a new Hub class:
public class EventHub : Hub { }
From for example a controller, call the hub:
private readonly IHubContext hub;
public ProjectQualityController(IConnectionManager connectionManager)
{
hub = connectionManager.GetHubContext<EventHub>();
}
public void SendMessage(string value)
{
hub.Clients.All.sendMessage(value);
}
In the javascript file, register to the sendMessage event:
<script src="scripts/jquery.signalR.js"></script>
<script src="signalr/hubs"></script>
<script type="text/javascript">
$(function () {
var chat = $.connection.eventHub;
chat.client.sendMessage = function (message) {
var encodedMsg = $('<div />').text(message).html();
alert(encodedMsg)
};
// Start the connection.
$.connection.hub.logging = true;
$.connection.hub.start()
.done(function(){ console.log('Now connected, connection ID=' + $.connection.hub.id); })
.fail(function(){ console.log('Could not Connect!'); });
});
</script>
Hope this helps someone!

Related

SignalR unable to connect from client to server with any available transport

I'm trying to connect to a websocket established with SignalR on my server (.NET).
My client (JavaScript) starts the negotiation, gets a response with the connectionId, connectionToken, etc., but afterwards is not able to connect with any of the available transport methods.
The last debug-trace I get is this:
[2022-11-17T10:21:02.093Z] Debug: HubConnection failed to start successfully because of error 'Error: Unable to connect to the server with any of the available transports. WebSockets failed: Error: There was an error with the transport. ServerSentEvents failed: Error: Error occurred LongPolling failed: Error'.
My server code:
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Sample
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_3_0).AddNewtonsoftJson();
services.AddSignalR().AddNewtonsoftJsonProtocol(opt => {
opt.PayloadSerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.WithOrigins("http://localhost:8080", "http://127.0.0.1:8080")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
}));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseFileServer();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseCors("MyPolicy");
app.UseMvc();
app.UseRouting();
app.UseEndpoints(routes =>
{
routes.MapHub<Controllers.DesignAutomationHub>("/api/signalr/designautomation");
});
}
}
}
Controller:
using Autodesk.Forge;
using Autodesk.Forge.DesignAutomation;
using Autodesk.Forge.DesignAutomation.Model;
using Autodesk.Forge.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Sample.Controllers
{
[ApiController]
public class ServiceController : Controller
{
// Design Automation v3 API
DesignAutomationClient _designAutomation;
// Used to access the application folder (temp location for files & bundles)
private IWebHostEnvironment _env;
// used to access the SignalR Hub
private IHubContext<DesignAutomationHub> _hubContext;
public ServiceController(IWebHostEnvironment env, IHubContext<DesignAutomationHub> hubContext, DesignAutomationClient api)
{
_designAutomation = api;
_env = env;
_hubContext = hubContext;
}
}
/// <summary>
/// Class uses for SignalR
/// </summary>
public class DesignAutomationHub : Microsoft.AspNetCore.SignalR.Hub
{
public string GetConnectionId() { return Context.ConnectionId; }
}
}
Client:
var connection;
var connectionId;
function startConnection(onReady) {
if (connection && connection.connectionState) {
if (onReady) onReady();
return;
}
connection = new signalR.HubConnectionBuilder()
.withUrl(
"http://<SERVERADRESS>/api/signalr/designautomation"
)
.configureLogging(signalR.LogLevel.Trace)
.build();
connection.start().then(function () {
connection.invoke("getConnectionId").then(function (id) {
connectionId = id;
if (onReady) onReady();
});
});
connection.on("downloadResult", function (url) {
console.log('Download result file here');
});
connection.on("onComplete", function (message) {
console.log(message);
});
}
I tested it locally with server and client on one machine, and all is working fine. But since the deployment test, I get the errors. (Websockets are activated on the server.)
Also Postman can establish a connection to the websocket, just my client fails.
I would appreciate any kind of help. Thanks in advance!
EDIT:
I also tried connecting to SignalR via the (here) described alternative to the SignalR client-side.
async function connectToWebsocket(negotiations) {
let token = encodeURIComponent(negotiations.connectionToken);
let wssPath = `ws://<SERVERADRESS>/api/signalr/designautomation?id=${token}`;
let ws = new WebSocket(wssPath);
console.log(ws);
}
async function connectToSignalR() {
$.ajax({
url: "<SERVERADRESS>/api/signalr/designautomation/negotiate?negotiateVersion=1",
contentType: "application/json",
dataType: "json",
headers: { "Access-Control-Allow-Origin": "*" },
type: "POST",
success: function (res) {
console.log(res);
connectToWebsocket(res);
},
error: function (error) {
console.log(error);
},
});
}
Still with the same outcome. I get the response from the negotiation but cant connect to the websocket.
As an additional Information. My "server" is an iis-express on a Azure-VM with a via ngrok forwarded adress.
ANOTHER EDIT:
My whole case about the use of SignalR is to get a websocket connection running, which the autodesk-forge servers can callback, when they finished my submitted task, to let them know what the next tasks are.
I added the Tag, maybe someone from this direction encountered same problems and could give a hint.
AND ANOTHER EDIT:
I now also tried the connection to the remote server with the simplest example I could find, the chatapp example from microsoft.
Still the same problems. I added the whole console output here:
Also I'm curious if theres maybe something wrong with my CORS.
But it's defined as stated in every working example...
The problem resulted from ngrok and probably CORS (as stated in the console output).
After a push in the right direction, I tried another tool (Conveyor - VS Extension) forwarding localhost to the net. There was absolutely no issue with connecting to the websocket or any other transport method from a remote client.
So for anybody with the same problem, trying to debug websocket connections forwarded with ngrok in iis-express and getting CORS errors, use Conveyor instead.
As long as you don't know how to configure ngroks CORS handling ("http --host-header=rewrite" wasn't doing the trick).
If you know how to configure ngroks CORS handling, I would be glad to read it in the comments.

Can´t establish connection to websocket "Whoops! Lost connection to http://localhost:8080"

I´m trying to implement a websocket in my spring boot application, but I´m unable to create a connection.
I used this video and its corresponding git-repo to create the following config for the server and the javascript code for the client.
Server
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gameplay");
registry.addEndpoint("/gameplay").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
Client
const url = 'http://localhost:8080';
let stompClient;
let paymentId;
function connectToSocket() {
console.log("Trying to open connection to /gameplay");
let socket = new SockJS("/gameplay");
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log("connected to the frame: " + frame);
stompClient.subscribe("/topic/game-progress", function (response) {
console.log("data");
let data = JSON.parse(response.body);
console.log(data);
})
})
}
The server-console doesn´t have any entries, so I guess there is something wrong with the javascript part. In the browser-console it says:
Trying to open connection to /gameplay
Opening Web Socket...
GET http://localhost:8080/gameplay/info?t=1620312392571 404
Whoops! Lost connection to http://localhost:8080/gameplay
I tried...
using different URLs to establish the connection
http://localhost:8080/gameplay
http://localhost:8080/app/gameplay
/gameplay
/app/gameplay
Using the URLs from the first bullet point to establish a connection using Chrome´s advanced REST client. I got the message "Unknown error occured"
Adding .setAllowedOrigins("*") to my stompEndpointRegistry like suggested here
Does anyone know...
Where the last part of the request (/info?t=1620312392571 )comes from and could it be causing the malfunction?
If I need to write "http:localhost.8080" before the socket URL? Some people do that, others don´t.
How I can get this working?
In others questions the root of the problem had something to do with the dependencies. I included all the dependencies that fixed the problem for other users so I. don´t think the dependencies are the problem. However, here is a link to my pom.xml.
I´m thankful for all kind of help.
I was also facing same issue. Update client code as
let socket = new SockJS("/app/gameplay");
Don't modify below server code
registry.addEndpoint("/gameplay").withSockJS();

Signalr server method is not calling using objHub.server

I am already googled about this but not found any solution.
My hub's methods are like this
public string Test(string hello)
{
return hello;
}
public override System.Threading.Tasks.Task OnConnected()
{
return base.OnConnected();
}
and my client side is
var objHub = $.connection.myHub;
$.connection.hub.start().done(function () {
objHub.server.test('test');
}).fail(function () {
/.....
})
OnConnected is calling perfectly but objHub.server.test is not calling.
Even I have checked in console for server side methods mapping to hub object and here is all server methods are registered with hub object
Why my server side methods are not calling?
Update
Now what I am seeing
Server is requesting to
http://localhost:83/signalr/connect?transport=longPolling&clientProtocol=1.5&connectionToken=MUM0NzA5MDI3QTEyRjM5RDM4QjEzNDhGRTFEMjJGNzI3QTcyQTRDM0ZDOTE3MTRCRUYwQkVCOUI3OEQ3Q0MxREY1NzNEQkUzQjAxM0QzMzlCRDIzQUY0OUJDNThENDVCMDUzQ0RENEMwQTUzNkNFMzEyNDY2QTkyMjExRkE4REVDMUZGRUE2RTdFNTNDRkM2NDg5NjEzMUIyMzQzNDI4Njk3RjRBNTdEMDlEQ0U1MUJGQ0I4RjE4Njg3NjU5NTBFRURGQTZCNzBGMzUwRjA0MzdFOERENkQ1NTFEQ0JCNEJDN0U3NDUyNA%3D%3D&connectionData=%5B%5D
And getting this error
Method not found: System.Threading.Tasks.Task 1<Microsoft.Owin.IFormCollection> Microsoft.Owin.OwinRequest.ReadFormAsync()
Thanks
Yes I updated my Microsoft.AspNet.SignalR2.2.2 when I revert to Microsoft.AspNet.SignalR2.2.0 then it working........

Angular Js and SignalR not working when called from WebAPI and Hub

I am working on signalR implementation with Angular JS. When i tried to call SignalR event from API or Hub to ui client side.. it is not working as expected.
WebAPI Call
I am not getting any response in browser for SignalR request.
ServiceCreated for SignalR
[Resposne On Event at client side][3]
I am not getting any resposne in test event for SignalR(Working some times in chrome).
Thanks for advance. Really Appreciate you quick answer.
You have to start the connection, additional to that you have to write your the "test" - function on the client which the server calls.
Here you see a sample:
Client Code:
<script>
$(function () {
var connection = $.hubConnection("http://localhost:27709/signalr");
var chatHubProxy = connection.createHubProxy('chatHub');
chatHubProxy.on('newMessageAdded', function (name, message) {
$("#messages").append("<li>" + name + message + "</li>");
console.log(name + ' ' + message);
});
connection.start({ transport: 'longPolling' }).done(function () {
$('#btnSend').click(function () {
chatHubProxy.invoke('newMessage', "Stephan", $('#msg').val());
$('#msg').val('').focus();
});
});
});
</script>
Server:
public class ChatHub : Hub
{
public void NewMessage(string name, string message)
{
Clients.All.newMessageAdded(name, message);
}
}
It's important that the you write the methode/function on server and client side same (no spelling mistakes).
Attention on Pascal and Camelcase of hubs, methods, ...:
- On the client the proxy name is a camel-cased version of the Hub class name.
By the way: Why you're not using the generated proxy?

SignalR callback does not trigger in JQuery UI widget

I am trying to create a JQuery UI widget that receives realtime updates from a server using SignalR (2.2.0). Invoking a method on the server works just fine, however invoking a client callback from the server does not trigger on the client.
I have enabled logging on the client as is suggested here: SignalR Troubleshooting and I can see in the console that the connection is setup just fine but the client method is never invoked. There is no error message of any kind. I have also defined the client method on the hub proxy before starting the connection like so:
_bindClientCallbacks: function () {
theHub.client.broadCastToClient = function (message) {
twr.log(message);
};
}
and afterwards I start the hub connection like so:
_startSignalRClient: function () {
$.connection.hub.logging = true;
$.connection.hub.start()
.done(function () {
twr.log("Connected to SignalR hub, id=" + $.connection.hub.id);
})
.fail(function () {
});
}
These methods are called in the '_create()' function in the JQuery widget like so:
_create: function () {
theHub = $.connection.DataImportHub;
this._bindClientCallbacks();
this._startSignalRClient();
}
This works fine and I can get a valid connection with an id. I can also call a server method from the client. But when I try to invoke the broadCastToClient method on the client from the server like so:
public void BroadCastToClient(string userId, string message)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<DataImportHub>();
foreach (var connectionId in _connections.GetConnections(userId))
{
hubContext.Clients.Client(connectionId).broadCastToClient(message);
}
}
Nothing happens on the client.. even though the server does find a valid connection that corresponds to the connection id I got on the client.
What am I missing here?
Just found out the solution by reading this post. Apparently having a custom SignalR dependency resolver setup in the Owin startup class breaks javascript callbacks. Moving the dependency resolver setup code to Application_Start in Global.asax does the trick. Why this happens really is beyond me...
Bad DI setup in Startup.cs
app.Map("/signalr", map =>
{
var hubConfiguration = new HubConfiguration
{
Resolver = new NinjectSignalRDependencyResolver(new StandardKernel())
};
map.RunSignalR(hubConfiguration);
});
Good DI setup in Global.asax
protected void Application_Start()
{
GlobalHost.DependencyResolver = new NinjectSignalRDependencyResolver(new StandardKernel());
}

Categories

Resources