Accessing hubs - Angularjs, asp.net mvc and SignalR - javascript

I am new in using SignalR and I find it hard to setup in my application.
App.js
I got code here in my app.run(); that checks whether a user was already logged in.
LoginUserService.isLogged().then(function(msg){
if (msg.data["authenticated"]) {
var id = $cookies.get('UserId');
var TestHub= $.connection.testHub;
$.connection.hub.start()
.done(function () {
//I get the id of online user and pass it on makeOnline method in my testHub
TestHub.server.makeOnline(id);
});
}
});
controller.js
Here in my controller I made connection to the same hub in my app.js in order for me to get the online clients. This controller can be accessed by admin only
app.controller('Users',function($scope){
var hub = $.connection.testHub;
hub.client.allOnline = function (users) {
console.log("client.allonline(" + JSON.stringify(users) + ")");
}
$.connection.hub.start()
.done(function () {
})
.fail(function (error) {
console.log(error);
});
});
TestHub.cs
private static ConcurrentDictionary<string, int> _locks = new ConcurrentDictionary<string, int>();
private static object _lock = new object();
public void MakeOnline(int userid)
{
lock (_lock)
{
foreach (int id in _locks.Values)
{
if (userid == id)
{
return;
}
}
_locks.AddOrUpdate(Context.ConnectionId, userid, (key, oldValue) => userid);
Clients.All.allOnline(_locks.Values);
}
}
My codes in app.js works but whenever I go to Users controller I can't get any result in hub.client.allOnline. I tried refreshing the browser and that's the only time I got result. Any ideas what should I do?

Related

SignalR client side method not firing on from server side button click

I am having a Visual Studio 2019 based SignalR Application, where client connects to server.
Following javascript function is called when a page is loaded and it connects to a running server with a successful connectionid.
function Connect() {
$.connection.hub.url = url;
stockTickerHubProxy = $.connection.mobileStockTickerHub;
if (stockTickerHubProxy) {
$.connection.hub.start().done(function () {
console.log("Connected...");
connectionId = $.connection.hub.id;
console.log(connectionId)
stockTickerHubProxy.server.setUserName(code);
})
.fail(function () {
alert("Can't connect");
})
;
stockTickerHubProxy.client.addMessage = function (name, message) {
console.log(name + ":" + message);
}
stockTickerHubProxy.client.showtradenotification = function (msg) {
alert(msg)
}
$.connection.hub.disconnected(function () {
console.log("Server disconnected.");
});
$.connection.hub.reconnecting(function () {
console.log("Server reconnecting...");
});
$.connection.hub.reconnected(function () {
console.log("Server reconnected...");
Connect();
});
$.connection.hub.error(function (error) {
console.log('SignalR error: ' + error)
});
}
}
On server, I am executing following test code for checking the function running in javascript html page. Following is the code.
private async void button1_ClickAsync(object sender, EventArgs e)
{
mhubContext = GlobalHost.ConnectionManager.GetHubContext<MobileStockTickerHub>();
await mhubContext.Clients.All.showtradenotification("Hello");
}
Following is the hub class MobileStockTickerHub
public class MobileStockTickerHub : Hub
{
//Called when a client is connected
public override Task OnConnected()
{
_users.TryAdd(Context.ConnectionId, Context.ConnectionId);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
string username;
_users.TryRemove(Context.ConnectionId, out username);
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
_users.TryAdd(Context.ConnectionId, Context.ConnectionId);
return base.OnReconnected();
}
public string SetUserName(string userName)
{
_users[Context.ConnectionId] = userName;
return "Received ping from " + userName;
}
}
Again, when button1_ClickAsync is fired, there is no activity on the webpage, that should fire showtradenotification with an alert message.
Let me know where I am wrong.
Thanks.
Move your client function before your hub.start. You should always register at least one function before your start. See the note regarding in the docs.

WebSocket stomp client subscribe for some devices are not working

I am trying to implement a spring boot chat application using WebSocket stomp client. If I send messages from one device to 4,5 devices then some are getting the messages and some are not. Some can send messages but don't receive any message and some are working completely fine. My application is running on wildfly server and the URL is over https.
Here is my js file. From my JSP page I am calling sendMsg with all parameter and through render method I am attaching the response with JSP using Handlebars.
if (!window.location.origin) {
window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
}
const url = window.location.origin+contextPath;
let stompClient;
let selectedUser;
let newMessages = new Map();
function connectToChat(userName, topicName) {
console.log("connecting to chat...")
let socket = new SockJS(url + '/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log("connected to: " + frame);
stompClient.subscribe("/topic/decision-log", function (response) {
let data = JSON.parse(response.body);
var msg = data.message;
var fromlogin = data.message;
render(data.username, msg, fromlogin);
});
});
}
connectToChat("1", "decision-log");
function sendMsg(from, text, username) {
stompClient.send("/app/chat/" + from, {}, JSON.stringify({
fromLogin: from,
message: text,
topicName: topicName,
username: username
}));
}
function render(username, message, projectId) {
var templateResponse = Handlebars.compile($("#message-response-template").html());
var contextResponse = {
username: username,
response: message,
date: date,
projectId: projectId
};
setTimeout(function () {
$chatHistoryList.append(templateResponse(contextResponse));
scrollToBottom();
}.bind(this), 1500);
}
Here is my WebSocket configuration file:
#Configuration
#EnableWebSocketMessageBroker
public class WebsocketConfiguration implements WebSocketMessageBrokerConfigurer{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").setAllowedOrigins("*").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app").enableSimpleBroker("/topic");
}
}
This is the controller. I always save all messages on the database that are coming through WebSocket that's why I can be sure that all devices can send messages as they have been saved on the database.
#Controller
#AllArgsConstructor
public class MessageController {
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
private final DecisionLogService decisionLogService;
#MessageMapping("/chat/{to}")
public void sendMessage(#DestinationVariable String to, MessageModel message, Authentication authentication ) {
simpMessagingTemplate.convertAndSend("/topic/decision-log", message);
AuthResponse userDetails = (AuthResponse) authentication.getDetails();
DecisionLogCreateRequest decisionLogCreateRequest = new DecisionLogCreateRequest();
decisionLogCreateRequest.setDecision(message.getMessage());
decisionLogCreateRequest.setProjectId(to);
ServiceResponseExtended<Boolean> response = decisionLogService.addDecisionLog(userDetails.getAccessToken(), decisionLogCreateRequest);
}
}
I can not find anything similar this issue. Please help me with right information and suggestion, and if anyone faced same kind of problem please share with me.
The problem was solved after configuring RabbitMQ Stomp Broker as a message broker instead of SimpleBroker.
Current WebSocket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebsocketConfiguration implements WebSocketMessageBrokerConfigurer{
#Value("${stomp-broker-relay-host}")
private String RELAY_HOST;
#Value("${stomp-broker-relay-port}")
private String RELAY_PORT;
#Value("${stomp-broker-login-user}")
private String USER;
#Value("${stomp-broker-login-pass}")
private String PASS;
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").setAllowedOrigins("*").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableStompBrokerRelay("/topic").setRelayHost(RELAY_HOST).setRelayPort(Integer.parseInt(RELAY_PORT)).setClientLogin(USER)
.setClientPasscode(PASS);
}
}
Reference:
https://medium.com/#rameez.s.shaikh/build-a-chat-application-using-spring-boot-websocket-rabbitmq-2b82c142f85a
https://www.javainuse.com/misc/rabbitmq-hello-world

It is possible to send a message to a specific user using SignalR?

I am new to SignalR, recently I am examining and learning how the following code works.
Link: https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-getting-started-with-signalr
I would like to know what is the easiest way to send a message to a specific user using the code in the previous link. Has anyone had experience or tried to do the same? I just need ideas since I don't know all the functionalities and methods that SignalR offers.
In my web application users have a unique username and I need to use that data for the connection. I have seen that there is a way to create groups and send the message in that way but I do not understand completely, a very simple example would help me a lot.
Can you help me or give me some advice? Thank you
I'm already write a blog here
So basically you need to
First define a hub method like this
public async Task Send(string userId)
{
var message = $"Send message to you with user id {userId}";
await Clients.Client(userId).SendAsync("ReceiveMessage", message);
}
Add to startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.Configure(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSignalR();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseSignalR(routes =>
{
routes.MapHub<ConnectionHub>("/connectionHub");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Then finally
(function () {
var connection = new signalR.HubConnectionBuilder().withUrl("/connectionHub").build();
connection.start().then(function () {
console.log("connected");
connection.invoke('getConnectionId')
.then(function (connectionId) {
sessionStorage.setItem('conectionId', connectionId);
// Send the connectionId to controller
}).catch(err => console.error(err.toString()));;
});
$("#sendmessage").click(function () {
var connectionId = sessionStorage.getItem('conectionId');
connection.invoke("Send", connectionId);
});
connection.on("ReceiveMessage", function (message) {
console.log(message);
});
})();
First I will need to make connection with the server using
new signalR.HubConnectionBuilder().withUrl(“/connectionHub”).build()
Then when the connection start I will invoke getConnectionId to get user connection id in the server and store into session storage because when we do refresh the browser signalR server will give you new connection id
Next when I click on the button I will invoke Send method in the server and I will listen
on “ReceiveMessage”
using connection.on(“ReceiveMessage”);
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}
https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/mapping-users-to-connections

SignalR js client wrong Server port

I'm starting with signalr but got some trouble, my test server run at http://localhost:22660/ and my web run at
http://localhost:61963/.I got this error when connect from client to server:
GET http://localhost:61963/signalr/negotiate?clientProtocol=1.4&connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&_=1496809403215 404 (Not Found)
I already config: $.connection.hub.url = 'http://localhost:22660/signalr'; but not work, this my js code:
var connection = $.hubConnection();
$.connection.hub.url = 'http://localhost:22660/signalr';
var chatHub = connection.createHubProxy('ChatHub');
connection.start()
.done(function () { console.log('Now connected, connection ID=' + connection.id); })
.fail(function () { console.log('Could not connect'); });
Server:
namespace test
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJSONP = true
};
map.RunSignalR(hubConfiguration);
});
}
}
}
namespace SignalRChat
{
public class ChatHub : Hub
{
public void Send(string name, string message)
{
// Call the addNewMessageToPage method to update clients.
Clients.All.addNewMessageToPage(name, message);
}
}
}
I dont sure, but i think because i using different version of signalr (2.1.2 and 2.2.0). Using same version solved my problem.

MEAN.io Strange $http.get Response

I am trying to develop a user management feature for a website using the MEAN.io stack. What I'm trying to do has worked in the past for other models on the same site so I'm not sure what is going on. My issue is that I am trying to get all the User models from the MongoDB database and pass them to an AngularJS controller so that I can display their information on a user management page. To that end, I added this function to the User controller in the backend:
exports.readUsers = function(req, res) {
var decodedToken = req.decodeToken;
var User = mongoose.model('User');
var id = req.params.id;
existUser(decodedToken, function(err, user) {
if(err) {
return sendError(res, 'INVALID_TOKEN');
}
User.find({})
.select('username')
.exec(function(err, results) {
if(err) {
return sendError(res, err);
}
res.json({success: true, userList: results});
});
});
}
This line to the routing code:
router.get('/users/all', Authorization.token, user.readUsers);
And this AngularJS controller for use with the frontend:
(function () {
"use strict";
var app = angular.module("GAP");
app.factory("UserEditFactory", function UserEditFactory($http, API_URL, AuthTokenFactory, $q) {
"use strict";
return {
readUsers: readUsers
};
//Get all users in the DB
function readUsers() {
if(AuthTokenFactory.getToken()) {
return $http.get(API_URL + "/users/all");
}else {
return $q.reject({
data: "valid token required"
});
}
}
});
app.controller("userPageController", ["UserEditFactory", "$scope", "$http", "$window", "API_URL",
function(UserEditFactory, $scope, $http, $window, API_URL) {
UserEditFactory.readUsers().then(function(data) {
console.log(data.data);
$scope.users = data.data.userList;
}, function(response) {
console.log(response);
});
}
]);
})();
When I load the page that is supposed to display this information, no data is displayed. I have determined that the AngularJS controller is calling the second function which I understand is the one used to respond to an error.
Further investigation of the object returned by the $http.get call reveals no data, and a status of -1. I'm not sure why this is happening, because I have used this exact pattern of code to get and display data from other models in the database on the same site. I can manually make HTTP calls to those working functions from this controller, and everything works fine. I'm not sure where to go from here or how to learn more about the issue. Can anyone offer insight? Let me know if you need more information.
Edit: As requested, here is the code for the AuthTokenFactory, which is an app.factory object in a common JS file.
app.factory('AuthTokenFactory', function AuthTokenFactory($window) {
'use strict';
var store = $window.localStorage;
var tokenKey = 'auth-token';
var userKey = "username";
return {
getToken: getToken,
setToken: setToken,
setUsername: setUsername
};
function getToken() {
return store.getItem(tokenKey);
}
function setToken(token) {
if (token) {
store.setItem(tokenKey, token);
} else {
store.removeItem(tokenKey);
}
}
function setUsername(username) {
if (username) {
store.setItem(userKey, username);
} else {
store.removeItem(userKey);
}
}
});

Categories

Resources