I use signalR in my project. It works fine. In a part on my project, I need to use instant notification when a delete button is clicked. The button is in jQuery datatable. I used the following code:
var connection = new signalR.HubConnectionBuilder().withUrl("/signalRServer").withAutomaticReconnect().build();
connection.start();
function DeleteData(pmId, mainFileName, fileName) {
if (confirm("Are you sure")) {
connection.invoke("PmPageUpdate", pmId).catch(function(err){
return console.error(err);
});
Delete(pmId, mainFileName, fileName);
} else {
return false;
}
}
When I debug, the hub method is not called. Console shows the following error:
PmManagement:692 Error: Failed to invoke 'PmPageUpdate' due to an
error on the server.
at H. (signalr.js:1:13973)
at L.I (signalr.js:1:14804)
at X.L.connection.onreceive (signalr.js:1:10649)
at WebSocket.r.onmessage (signalr.js:1:27565)
How can I fix this?
Update:
The followings are codes I've written for the hub. Some methods that are not related to this question has been ignored.
Hub:
private readonly IUserRepository _userRepository;
private readonly ICostCenterRepository _costcenterRepository;
private readonly IHttpContextAccessor _httpContext;
private readonly INotificationRepository _notificationRepository;
private readonly IMessageRepository _messageRepository;
private readonly IPmRepository _pmRepository;
public MessageHub(IUserRepository userRepository, ICostCenterRepository costcenterRepository, IHttpContextAccessor httpContext,
INotificationRepository notificationRepository, IMessageRepository messageRepository, IPmRepository pmRepository)
{
_userRepository = userRepository;
_costcenterRepository = costcenterRepository;
_httpContext = httpContext;
_notificationRepository = notificationRepository;
_messageRepository = messageRepository;
_pmRepository = pmRepository;
}
//define a dictionary to store the userid.
private static Dictionary<string, List<string>> NtoIdMappingTable = new Dictionary<string, List<string>>();
public Task JoinNotificationGroup(string groupName)
{
return Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
public Task LeaveNotificationGroup(string groupName)
{
return Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
}
public override async Task OnConnectedAsync()
{
var username = Context.User.Identity.Name;
var userId = Context.UserIdentifier;
List<string> userIds;
//store the userid to the list.
if (!NtoIdMappingTable.TryGetValue(username, out userIds))
{
userIds = new List<string>();
userIds.Add(userId);
NtoIdMappingTable.Add(username, userIds);
}
else
{
userIds.Add(userId);
}
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
var username = Context.User.Identity.Name;
//remove userid from the List
if (NtoIdMappingTable.ContainsKey(username))
{
NtoIdMappingTable.Remove(username);
}
await base.OnDisconnectedAsync(exception);
}
string username = _userRepository.GetUsernameByCostCenter(pmId).ToString();
var userId = NtoIdMappingTable.GetValueOrDefault(username);
await Clients.User(userId.ToString()).SendAsync("SignalReceiver");
}
Related
I have been dealing this error for a long time. I just want to make signalr to listen my 2 method. When i comment the (this) methods it works perfectly.
But it doesnt work. Can someone help me? Sometimes i can get the value but then it gets me the error like below the image. I researched some pages but cant find any answer actually.
Vuejs
const hubConnection = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Debug)
.withUrl("http://localhost:7002/ChatHub", {
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets,
})
.build();
hubConnection.start();
this.connectionId = hubConnection.connectionId;
this.connection = hubConnection;
hubConnection.on("UserConnected",(users) => console.log(users));(this)
hubConnection.on(categoryId, (all) => {
all = JSON.parse(all);
this.userMessage = {
id: all.Id,
text: all.Text,
userId:all.UserId,
userName:all.UserName,
categoryName: all.CategoryName,
createdOn: all.CreatedOn,
};
this.messages.push(this.userMessage);
console.log(this.userMessages);
});
},
using Microsoft.AspNetCore.SignalR;
using System.Text;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.AspNetCore.Http;
using System;
using System.Web;
using System.Net.Http.Headers;
using System.Linq;
using System.IdentityModel.Tokens.Jwt;
using System.IdentityModel.Tokens;
using System.Collections.Generic;
using System.Threading.Tasks;
using Message.Dal.Concrete;
using Message.Dal.Abstract;
using Message.Dal.Model;
using Microsoft.Extensions.Configuration;
namespace Message.Dal.SignalRHub
{
public class ChatHub : Hub
{
private readonly IHubContext<ChatHub> _chatHub;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IElasticRepository<OnlineUserModel> _elasticRepository;
private readonly string _indexName;
public ChatHub(IHubContext<ChatHub> chatHub,IElasticRepository<OnlineUserModel> elasticRepository,IConfiguration configuration)
{
_chatHub = chatHub;
_httpContextAccessor = new HttpContextAccessor();
_elasticRepository = elasticRepository;
_indexName = configuration["elasticsearchserver:User"].ToString();
}
public async Task SendMessage(Guid categoryId,string message)
{
await _chatHub.Clients.All.SendAsync(categoryId.ToString(),message);
}
public override Task OnConnectedAsync()
{
var token = string.Empty;
var httpContext = _httpContextAccessor.HttpContext.Request.Cookies;
var onlineUserModel = new OnlineUserModel();
onlineUserModel.Id = Context.ConnectionId;
if(!httpContext.Any())
{
return Task.CompletedTask;
}
token = httpContext.Where(x=> x.Key == "CodeChatBackend").FirstOrDefault().Value;
if(AuthenticationHeaderValue.TryParse(token,out var headerVal))
{
var handler = new JwtSecurityTokenHandler();
var val = handler.ReadJwtToken(headerVal.ToString());
onlineUserModel.UserName = val.Claims.FirstOrDefault(x => x.Type == "Name").Value;
}
var checkOnlineUser = _elasticRepository.GetUserAsync(onlineUserModel.UserName,_indexName);
if(checkOnlineUser.Result == null)
{
_elasticRepository.CreateUserAsync(onlineUserModel.Id,onlineUserModel,_indexName);
}
var getOnlineUser = _elasticRepository.GetAllAsync(_indexName);
_chatHub.Clients.All.SendAsync("UserConnected",getOnlineUser);(this)
base.OnConnectedAsync();
return Task.CompletedTask;
}
public override Task OnDisconnectedAsync(Exception exception)
{
var httpContext = _httpContextAccessor.HttpContext.Request.Cookies;
var token = string.Empty;
var result = _elasticRepository.DeleteUserAsync(Context.ConnectionId,_indexName);
var getOnlineUser = _elasticRepository.GetAllAsync(_indexName);
_chatHub.Clients.All.SendAsync("UserConnected",getOnlineUser);(this)
base.OnDisconnectedAsync(exception);
return Task.CompletedTask;
}
}
}
When i delete the onconnectedasync client method. It works perfectly. Am i missing something?
I fixed it! My mistake totally. I was trying to make one hub to connect different methods. To fix this: Create second hub class and configure your startup, then call hub method with a new connection.
I'm trying to make a web socket connection between js and java, but I get this answer:
Uncaught DOMException: An attempt was made to use an object that is not, or is no longer, usable
I did it based in some samples on internet. Someone have some idea what can it be?
Project Name is Teste-1
JS Code
var socket = null;
function init(){
socket = new WebSocket("wss://localhost:8080/Teste-1/task");
socket.onmessage = onMessage;
}
function onMessage(event){
var task = JSON.parse(event.data);
if(task.action === "add"){
document.getElementById("point").innerHTML += event.data;
}
}
function teste() {
var action = {
action: "add",
name: "Test",
description: "This is just a test"
};
socket.send(JSON.stringify(action));
}
window.onload = init;
HTML Code
<html>
<head>
<title>Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<button onclick="teste()">Teste</button>
<div id="point"></div>
<script src="websocket.js"></script>
</body>
</html>
JAVA Codes
public class Task implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private String description;
public Task(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
#ServerEndpoint(value="/task")
public class TaskSocket {
#Inject
private TaskSessionHandler handler;
#OnOpen
public void open(Session session){
handler.addSession(session);
}
#OnClose
public void close(Session session){
handler.removeSession(session);
}
#OnError
public void onError(Throwable error){
Logger.getLogger(TaskSocket.class.getName()).log(Level.SEVERE, null, error);
}
#OnMessage
public void handleMessage(String message, Session session) {
try (JsonReader reader = Json.createReader(new StringReader(message))) {
JsonObject jsonMessage = reader.readObject();
if ("add".equals(jsonMessage.getString("action"))) {
Task task = new Task();
task.setName(jsonMessage.getString("name"));
task.setDescription(jsonMessage.getString("description"));
handler.addTask(task);
}
}
}
}
#ApplicationScoped
public class TaskSessionHandler {
//Each client connected to the application has its own session.
private final Set<Session> sessions = new HashSet<>();
private final Set<Task> tasks = new HashSet<>();
public void addSession(Session session) {
sessions.add(session);
for(Task task : tasks){
JsonObject addMessage = createJSON(task);
sendToSession(session, addMessage);
}
}
public void removeSession(Session session) {
sessions.remove(session);
}
public List<Task> getTasks(){
List<Task> list = new ArrayList<Task>(tasks);
return list;
}
public void addTask(Task e){
tasks.add(e);
JsonObject message = this.createJSON(e);
sendToAllConnectedSessions(message);
}
private JsonObject createJSON(Task task){
JsonProvider provider = JsonProvider.provider();
JsonObject message = provider.createObjectBuilder()
.add("action", "add")
.add("name",task.getName())
.add("description",task.getDescription()).build();
return message;
}
private void sendToAllConnectedSessions(JsonObject message) {
for (Session session : sessions) {
sendToSession(session, message);
}
}
private void sendToSession(Session session, JsonObject message) {
try {
session.getBasicRemote().sendText(message.toString());
} catch (IOException ex) {
sessions.remove(session);
Logger.getLogger(TaskSessionHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
I got the same error message due to the websocket.send() being executed before the connection to the webSocket server was established and open.
My contribution would be the advice to make sure messages aren't sent until the connection is open, so that the server actually can receive them.
I cannot tell whether that was the problem in the question asked though. However I faced a very similar problem and ended up here, and wanted to share what worked for me.
In my case the code that didn't work looked like this:
const exampleSocket = new WebSocket('wws://example')
exampleSocket.onopen = function (event) {
console.log('connection is open!')
}
exampleSocket.send(JSON.stringify(msg))
exampleSocket.onmessage = function (event) {
console.log('message event data looks like this: ', event.data)
}
What worked was moving the code that sent a message into the onopen callback function.
const exampleSocket = new WebSocket('wws://example')
exampleSocket.onopen = function (event) {
console.log('connection is open!')
exampleSocket.send(JSON.stringify(msg)) // <- this is the change
}
exampleSocket.onmessage = function (event) {
console.log('message event data looks like this: ', event.data)
}
Sidenote: the readyState property of websocket could be useful when dealing with making sure the server is ready to receive messages.
If it is 1, it means connection is open, so it could be used like this:
if (exampleSocket.readyState === 1) {
console.log("It is safe to send messages now")
}
I wrote code for example "SMS Retreiver API" https://developers.google.com/identity/sms-retriever/request
but I don`t result which I wont
This code past to MainActivity.
SmsRetrieverClient client = SmsRetriever.getClient(this);
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
}
});
task.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});
This code past to MySMSBroadcastReceiver.
public class MySMSBroadcastReceiver extends BroadcastReceiver {
String message;
Status status;
private static MessageListener mListener;
#Override
public void onReceive(Context context, Intent intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
switch(status.getStatusCode()) {
case CommonStatusCodes.SUCCESS:
// Get SMS message contents
message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
// Extract one-time code from the message and complete verification
// by sending the code back to your server.
break;
case CommonStatusCodes.TIMEOUT:
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
break;
}
mListener.MySMSBroadcastReceiver(message);
}
}
public static void bindListener(MessageListener listener){
mListener = listener;
}
}
In my Manifest
<receiver android:name="ru.project.MBank.MySMSBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
</intent-filter>
</receiver>
But result get nothing.
Help what do I do wrong?
I had same problem. First you need to generate a unique key (App Signature) that will identify message and your device. Once you generate key, your broadcaster will be able to detect message.
public class AppSignature extends ContextWrapper {
public static final String TAG = AppSignature.class.getSimpleName();
private static final String HASH_TYPE = "SHA-256";
public static final int NUM_HASHED_BYTES = 9;
public static final int NUM_BASE64_CHAR = 11;
public AppSignature(Context context) {
super(context);
}
/**
* Get all the app signatures for the current package
* #return
*/
public ArrayList<String> getAppSignatures() {
ArrayList<String> appCodes = new ArrayList<>();
try {
// Get all package signatures for the current package
String packageName = getPackageName();
PackageManager packageManager = getPackageManager();
Signature[] signatures = packageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES).signatures;
// For each signature create a compatible hash
for (Signature signature : signatures) {
String hash = hash(packageName, signature.toCharsString());
if (hash != null) {
appCodes.add(String.format("%s", hash));
}
}
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Unable to find package to obtain hash.", e);
}
return appCodes;
}
private static String hash(String packageName, String signature) {
String appInfo = packageName + " " + signature;
try {
MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
byte[] hashSignature = messageDigest.digest();
// truncated into NUM_HASHED_BYTES
hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
// encode into Base64
String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);
return base64Hash;
} catch (NoSuchAlgorithmException e) {
}
return null;
}
}
After this initiate this class in your firs activity.
Hope this will help you.
I just got handed a new project where most of the job was already done but I needed to change some things for my project. I have a self hosted server setup like this in a consoleapp:
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
and a route configured like this:
private static HttpSelfHostConfiguration CreateWebServerConfiguration()
{
var config = new HttpSelfHostConfiguration(string.Format("http://{0}:{1}", Environment.MachineName, 80));
config.Routes.MapHttpRoute("Api", "api/{controller}/{id}/{value}", new {id = RouteParameter.Optional, value = RouteParameter.Optional });
config.Routes.MapHttpRoute("Defect", "defect/{action}/{id}", new { controller = "Defect", action = RouteParameter.Optional, id = RouteParameter.Optional });
config.Routes.MapHttpRoute("Content", "content/{action}/{file}", new { controller = "Content"});
config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}/{date}", new { controller = "Home", action = "Index", id = RouteParameter.Optional, date = RouteParameter.Optional });
var templateConfig = new TemplateServiceConfiguration { Resolver = new DelegateTemplateResolver(name => ReadFileContent("Views", name))};
Razor.SetTemplateService(new TemplateService(templateConfig));
return config;
}
This works perfectly, but I ran into a situation, where I didn't want the front end to pull with a timer on the server(currently auto refresh every 5 min). I want the server to update the frontend, when there is something new to update. I found the solution to this which would be websockets, but I have a lot of problems using those.
my .js file:
(function() {
if ("WebSocket" in window) {
alert("WebSocket is supported by your Browser!");
} ws://' + window.location.hostname + window.location.pathname.replace('index.htm', 'ws.ashx') + '?name='
var socket = new WebSocket("ws://localhost:80/WebsocketServer.cs");
socket.onmessage = function(event) {
alert("message recv.");
alert(event.data);
};
socket.onopen = function() {
alert("open");
}
socket.onerror = function(errorEvent) {
alert("Error");
alert(errorEvent);
};
socket.onclose = function(closeEvent) {
alert(closeEvent.code);
}
})();
I found multiple examples like this one: https://blog.simpleisbest.co.uk/2012/05/01/websockets-with-asp-net-4-5-and-visual-studio-11/ but it doesn't seem to work for me, this is my files:
WebsocketServer.cs
public class WebsocketServer : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
{
context.AcceptWebSocketRequest(new WebSocketManager());
}
}
public bool IsReusable
{
get { return false; }
}
}
WebSocketManager.cs
public class WebSocketManager : WebSocketHandler
{
public static WebSocketCollection clients = new WebSocketCollection();
public override void OnClose()
{
clients.Remove(this);
}
public override void OnError()
{
}
public override void OnMessage(string message)
{
}
public override void OnOpen()
{
clients.Add(this);
clients.Broadcast("Connected");
}
}
The frontend returns closing code 1006 or CLOSE_ABNORMAL. My guess is the link to the backend isn't created?
I have made SignalR custom methods to redirect users if they want to join group that already has 2 members. Everything seems to be working fine except the redirect method.
ChatHub.cs:
namespace SignalRChat
{
public class ChatHub : Hub
{
static string test2 = "";
public static Dictionary<string, int> rooms = new Dictionary<string, int>();
public void Test(string groupName)
{
if (!rooms.ContainsKey(groupName))
{
rooms.Add(groupName, 1);
}
else if(rooms[groupName] != 2)
{
rooms[groupName] = 2;
}
else
{
test2 = "testing";
Redirect();
}
}
public Task Redirect()
{
return Clients.Caller.redirectTo();
}
public Task JoinGroup(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
}
}
}
Scripts:
var chat2 = $.connection.chatHub;
$.connection.hub.start().done(function () {
chat2.server.test(roomId);
chat2.client.redirectTo = function () {
window.location.replace("http://stackoverflow.com");
}
chat2.server.joinGroup(roomId);
});
When there are already 2 clients in a group, test2 is set to "testing" but client does not get redirected.
Change your scripts to:
var chat2 = $.connection.chatHub;
// var roomId = "R1" <-- I added this for testing
chat2.client.redirectTo = function () {
window.location.replace("http://stackoverflow.com/questions/35848709/signalr-custom-method-to-redirect#35857376");
}
$.connection.hub.start().done(function () {
chat2.server.joinGroup(roomId);
chat2.server.test(roomId);
});
Note: in your Test method the logic says that the redirect will only run if rooms dictionary contains the given roomname and the int value corresponding to that roomname is '2'. Probably not your real planned logic.
For testing I added to the backed code:
public static Dictionary<string, int> rooms = new Dictionary<string, int>();
public void Test(string groupName) // <-- I sent "groupName: R1" from js
{
rooms["R1"] = 2;
if ...
}