I'm working on an ASP.NET Boilerplate project. I want to integrate SignalR properly to this project.
There is the documentation, but I want to know the flow of SignalR, when sending notifications from the back-end to the front-end. Any sample code or suggestions are appreciated.
ASP.NET Boilerplate is open-source, so you can look at the code on GitHub.
From the documentation: Abp.AspNetCore.SignalR package implements IRealTimeNotifier.
The code below is for ASP.NET Core & jQuery version, but the flow is the same for other versions.
On the back-end, NotificationDistributer injects and calls IRealTimeNotifier:
await RealTimeNotifier.SendNotificationsAsync(userNotifications.ToArray());
SignalRRealTimeNotifier implements SendNotificationsAsync, which gets the online clients for each user and invokes the client-side method with the userNotification as a parameter:
var onlineClients = _onlineClientManager.GetAllByUserId(userNotification);
foreach (var onlineClient in onlineClients)
{
var signalRClient = _hubContext.Clients.Client(onlineClient.ConnectionId);
signalRClient.InvokeAsync("getNotification", userNotification);
}
The SignalR client registers for getNotification and triggers 'abp.notifications.received' with the notification as a parameter:
connection.on('getNotification', function (notification) {
abp.event.trigger('abp.notifications.received', notification);
});
The module-zero-core-template has main.js that registers a notification handler:
abp.event.on('abp.notifications.received', function (userNotification) {
abp.notifications.showUiNotifyForUserNotification(userNotification);
}
showUiNotifyForUserNotification formats and shows the notification on the front-end:
abp.notifications.showUiNotifyForUserNotification = function (userNotification, options) {
var message = abp.notifications.getFormattedMessageFromUserNotification(userNotification);
var uiNotifyFunc = abp.notifications.getUiNotifyFuncBySeverity(userNotification.notification.severity);
uiNotifyFunc(message, undefined, options);
}
Related
I'm currently trying to get push notifications working for my mobile app using Azure Notification Hubs. Android is working fine and the initial iOS set up in AppDelegate works ok with a sample tag.
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
if (deviceToken == null)
{
return;
}
SBNotificationHub hub = new SBNotificationHub(CommonConstants.LISTEN_CONNECTION_STRING, CommonConstants.NOTIFICATION_HUB_NAME);
// update registration with Azure Notification Hub
hub.UnregisterAll(deviceToken, async (error) =>
{
if (error != null)
{
System.Diagnostics.Debug.WriteLine($"Unable to call unregister {error}");
return;
}
string[] tags = new[] { "iostestpush" };
NSSet userTags = new NSSet(tags);
hub.RegisterNative(deviceToken, userTags, (error) =>
{
if (error != null)
{
System.Diagnostics.Debug.WriteLine($"Unable to call register {error}");
return;
}
});
var templateExpiration = DateTime.Now.AddDays(120).ToString(System.Globalization.CultureInfo.CreateSpecificCulture("en-US"));
hub.RegisterTemplate(deviceToken, "defaultTemplate", CommonConstants.APN_TEMPLATE_BODY, templateExpiration, userTags, (errorCallback) =>
{
if (errorCallback != null)
{
System.Diagnostics.Debug.WriteLine($"RegisterTemplateAsync error: {errorCallback}");
}
});
});
}
The issue I'm having is I need to register the UserId after a successful login. So I set up a service with the above code, saved the token to the device as string so it can be retrieved in the service and turned back into an NSData token
NSData deviceToken = new NSData(token, NSDataBase64DecodingOptions.None);
After a successful login I send the token string and the tag array to my service.
string[] userTag = new[] { loginResponse.UserId.ToString() };
await this._azureReg.SendRegistrationToServer(deviceToken, userTag);
Which, other than turning the token back into NSData and the user tag into an NSSet, is the same as above other than the name change. But Azure is claiming there is no registration even though my output shows
Registered for push notifications with token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
I thought it was the string conversion back and forth, so tested that in the AppDelegate and it worked fine.
So, I'm at a loss at how to register the UserId after a successful login and why it works in one place but not the other.
I hope that's clear and thanks for any advice in advance.
You probably ran into the same bug as me and several others.
Basically SBNotificationHub method overloads like UnregisterAll and RegisterTemplate with the callback signature do not work when you use them off the main thread, using the libraries to date. I was also using a Service for the same purpose (to handle push across platforms with different tags, especially for user id) but my implementation involved switching off the main thread for this.
The bug we logged and is now being addressed is here: https://github.com/Azure/azure-notificationhubs-ios/issues/95
The solution, for now, is to ditch SBNotificationHub completely. The Xamarin / Azure documentation is out of date, and SBNOtificationHub is legacy code. The recommended library is MSNotificationHub. https://github.com/azure/azure-notificationhubs-xamarin
As workarounds you can use the SBNotificationHub method overloads that do not involve callbacks (they return an error message instead) or the workaround in the 95 issue above.
Hi I'm trying to send a message to a group using the Azure Signal R Serverless JS Client Js Library.
I can do this from the Azure Serverless Function as simply as:
await signalRMessages.AddAsync(
new SignalRMessage
{
GroupName = m.GroupName,
Target = m.Target,
Arguments = new[] { m.Message }
});
*where signalRMessages = IAsyncCollector signalRMessages
How can I send this same message from the js library?
trying to send a message to a group using the Azure Signal R Serverless
You can refer to this github repo that shows with sample code how to implement group broadcasting functionality in Azure functions with Azure SignalR Service.
Add user to a group using the SignalRGroupAction class
return signalRGroupActions.AddAsync(
new SignalRGroupAction
{
ConnectionId = decodedfConnectionId,
UserId = message.Recipient,
GroupName = message.Groupname,
Action = GroupAction.Add
});
On client side, make request to endpoint to add a user to a group
function addGroup(sender, recipient, connectionId, groupName) {
return axios.post(`${apiBaseUrl}/api/addToGroup`, {
connectionId: connectionId,
recipient: recipient,
groupname: groupName
}, getAxiosConfig()).then(resp => {
if (resp.status == 200) {
confirm("Add Successfully")
}
});
}
Test Result
Updated:
Q: "send the message from the JS Client straight from the socket".
A: From here, we can find:
Although the SignalR SDK allows client applications to invoke backend logic in a SignalR hub, this functionality is not yet supported when you use SignalR Service with Azure Functions. Use HTTP requests to invoke Azure Functions.
It's seems like this is now possible ...
https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-concept-serverless-development-config#sending-messages-from-a-client-to-the-service
Sending messages from a client to the service If you have upstream
configured for your SignalR resource, you can send messages from
client to your Azure Functions using any SignalR client. Here is an
example in JavaScript:
JavaScript
connection.send('method1', 'arg1', 'arg2');
I work on a ASP.NET 5 (ASP.NET vNext) website. I use SignalR server (1.0 Beta 3) for some processing. It it is correctly set up because I can successfully invoke server methods from a Javascript browser client.
But when a I use .NET client (.NET 4.5 with SignalR 2.2.0), the method invoke fail with the generic "error 500".
I have downloaded both SignalR server and client sources to be able to debug them. I have seen that DefaultHttpClient.Post() client method is called with valid "postData" parameter, but the server PersistentConnectionMiddleware.Invoke() method has a http context without any "Form" value inside the request. And it makes SignalR server side failing in the ForeverTransport.ProcessSendRequest() method.
The post form seems to be forgotten during the transfer between the client and the server (I use the default IIS Express server).
Any idea? Thank you...
I saw the issue you opened and have committed a fix/workaround.
At the moment it doesn't look like SignalR 3 works with the client due to the fact it expects all requests to be form encoded. The workaround is to update ProcessSendRequest() so it can get the non-form-encoded data from the 2.2 .NET client;
protected virtual async Task ProcessSendRequest()
{
var data = await GetData().PreserveCulture();
if (Received != null)
{
await Received(data).PreserveCulture();
}
}
private async Task<string> GetData()
{
if (Context.Request.HasFormContentType)
{
var form = await Context.Request.ReadFormAsync().PreserveCulture();
return form["data"];
}
else
{
var stream = new System.IO.StreamReader(Context.Request.Body);
var output = await stream.ReadToEndAsync().PreserveCulture();
var decoded = UrlDecoder.UrlDecode(output);
return decoded.Replace("data=", "");
}
}
I'm working an ASP.net MVC cloud service project running two roles, a web role and a worker role. One of the pages in the web role initiate a request to build an APK file, building an APK file on the server can take anywhere from 1-5 minutes. So we came up with the following flow:
The user initiate the APK building process on the page.
The request is routed to our mvc action, creating a new message on an Azure Storage Queue.
The Worker role is always polling from the queue and starts the APK building process. Now that the APK is ready we want ideally to notify the user by:
(a) sending an email, which is working now. and (b) notifying the user on the page using SignalR.
Our problem is now in the SignalR part, how can we notify the user on the page that the APK is ready and he can download it.
EDIT - Copying contents of the first comment for the sake of completeness -
I've looked the question again and I understand that you are using a worker role to poll the queue. In this case, you can make your work role a .Net SignalR client that connects to the APK signalR hub on the web role. The signlaR hub on the web role can simple forward any message it receives from the .Net client to the javascript client (browser).
I would recommend going through the below links
Hubs API Guide - Server
Hubs API Guide - Javascript Client
before going through rest of the answer.
As can be understood from the above two links, SignalR enables the server to 'push' data to the client. In order for this to happen, you require two things -
A signalR hub - this is the 'hub' to which clients can subscribe to in order to receive messages.
A client connected to the hub
Your signalR hub on the server can look something like this -
public class APKHub : Hub
{
public async Task JoinGroup(string groupName)
{
await Groups.Add(Context.ConnectionId, groupName);
Clients.Group(groupName).sendMessage(Context.User.Identity.Name + " joined.");
}
public Task LeaveGroup(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
public void NotifyUser(string userId)
{
this.Clients.Group(userId).notify();
}
}
On the client, your code might look something like this -
var notificationHandler = function () {
var url;
var user;
var init = function (notificationUrl, userId) {
url = notificationUrl;
user = userId;
connectToAPKHub();
}
var connectToAPKHub = function () {
$.connection.hub.url = url;
var apk= $.connection.apkHub;
apk.client.notifyUser = function (user) {
console.log(user);
}
apk.client.addMessage = function (message) {
console.log(message);
}
$.connection.hub.start().done(function () {
console.log('connected to apkhub');
apk.server.joinGroup(user);
})
}
return {
init: init
}
}();
The notificationUrl is the URL that the signalR server is listening to.
This sets up your basic hub on the server and you should now be able to connect your client to the signalR hub. When the APK is built, you can use the following code (place it anywhere - for ex - in a controller action) to actually push a message to the concerned client -
var apkHub = GlobalHost.ConnectionManager.GetHubContext<APKHub>();
apkHub.Clients.Group(groupName).notifyUser(groupName);
The groupName can be an identifier that uniquely identifies a user.
Hope this helps.
I want to implement a facebook like notification system in ASP.NET MVC 3 : notifications are sent to a specific user to notify him for an action on one of his items.
Is signalr suited for such requirement?
How could i send a notification to a specific user (all opened sessions of this user) using SignalR?
Edit
Ok, Here what i did
In the client side
$(function () {
// Proxy created on the fly
var chat = $.connection.chat;
var username = '#Html.ViewContext.HttpContext.User.Identity.Name';
// Declare a function on the chat hub so the server can invoke it
chat.addMessage = function (message) {
$('#messages').append('<li>' + message + '</li>');
};
// Start the connection
$.connection.hub.start(function (){
chat.join(username);
});
});
In the server side
public class Chat : Hub
{
public void Join(string username)
{
AddToGroup(username);
}
}
And every time i need to notify a user in the controller i do the following:
IConnectionManager connectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
dynamic clients = connectionManager.GetClients<Chat>();
clients[username].addMessage("test");
Yes, SignalR is a good choice for that. Take a look at the documentation regarding Hubs (server and JS client).
You need to implement the server logic to associate your client's session with SignalR's session. You can use groups to notify all the open sessions of each user.
It is appropriate for this or you use polling, those are the two choices.
Heres a brand new video from today on this:
http://channel9.msdn.com/Shows/Web+Camps+TV/Damian-Edwards-and-David-Fowler-Demonstrate-SignalR?utm_source=dlvr.it&utm_medium=twitter