I am trying to use signalR as a messaging system for update of progress bars etc on a web page - and I need to be able to isolate the progess ( or whatever message ) to a specific page.
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<InventoryHub>();
Messages like>> hubContext.Clients.All.InitialiseProgress(inventoryData.Count());
Obviously the problem is Clients.All - I am just not sure how I wire it up so that when it sends back from the server to client - it only sends to the page it was called from. I can't get my head around the connectionid.
Please help :-)
You could use SignalR Groups to broadcast progress messages to specific pages.
From Working with Groups in SignalR:
Adding a connection ID to a group:
public class ContosoChatHub : Hub
{
public Task JoinRoom(string roomName)
{
return Groups.Add(Context.ConnectionId, roomName);
}
public Task LeaveRoom(string roomName)
{
return Groups.Remove(Context.ConnectionId, roomName);
}
}
Broadcasting to a specific group:
Clients.Group(groupName).addChatMessage(name, message);
Related
I created Azure Notification Hub and added the server key from Firebase to the section "GCM/FCM" in notification hub. After which, I used the shared access key and notification hub name to create the installation (following the Azure documentation). Here's the code that I used:
[FunctionName("TestFunction")]
public static async Task<IActionResult> RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]
HttpRequest req, ILogger log)
{
var deviceUpdate = new DeviceInstallation()
{
installationId = "<myInstallationid>",
pushChannel = "<DeviceTokenGeneratedByFirebase>",
platform = "fcm",
tags = new string[] {"notificationhubtag1"},
};
var responseMessage = new HttpResponseMessage();
try
{
responseMessage = await Put(deviceUpdate);
}
catch
{
log.LogInformation("exception occured");
}
return new OkObjectResult(responseMessage);
}
// Custom API
public static async Task<HttpResponseMessage> Put(DeviceInstallation deviceUpdate)
{
NotificationHubClient hub = new NotificationHubClient(fullAccessConnString, hubName);
Installation installation = new Installation();
installation.InstallationId = deviceUpdate.installationId;
installation.PushChannel = deviceUpdate.pushChannel;
installation.Tags = deviceUpdate.tags;
switch (deviceUpdate.platform)
{
case "mpns":
installation.Platform = NotificationPlatform.Mpns;
break;
case "wns":
installation.Platform = NotificationPlatform.Wns;
break;
case "apns":
installation.Platform = NotificationPlatform.Apns;
break;
case "fcm":
installation.Platform = NotificationPlatform.Fcm;
break;
default:
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
await hub.CreateOrUpdateInstallationAsync(installation);
return new HttpResponseMessage(HttpStatusCode.OK);
}
public class DeviceInstallation
{
public string installationId { get; set; }
public string platform { get; set; }
public string pushChannel { get; set; }
public string[] tags { get; set; }
}
When I run this code, I get a successful message back. The Azure portal itself does not show much information about the no. of devices registered or active devices information in the Azure notification hub. However, I was able to confirm that the installation exists by making a GET request to the installation API directly through this call:
https://<myNotificationHubNameSpace>.servicebus.windows.net/<myHubName>/installations/<myInstallationId>/?api-version=2015-01
This call returned me a 200 OK with the installation I created from the earlier step. The response looked like this:
{"installationId":"<myInstallationId>","pushChannel":"<DeviceTokenGeneratedByFireBase>","pushChannelExpired":false,"platform":"gcm","expirationTime":"9999-12-31T23:59:59.9999999Z","tags":["notificationhubtag1"]}
So, I went to the Azure Notification Hub and sent a test message from the "Test Send" tab and used the tag "notificationhubtag1" in the SendTo tags field. I got back the successful message that said "The Notification was successfully sent to the Push Notification System" and also got the Registration number.
However, I don't see any notifications being sent to the app itself. How can I debug more information about this specific message being pushed. Where did it get pushed?
Is there any way to find more information on the pushed messages, installed devices etc on the notification hub itself? I found an old post about checking logs which said to switch to standard tier instead of free Tier for more information. I have made the switch to standard tier but I don't see any difference in the way overview or activity logs is displayed to me between Free or Standard Tier.
Notification Hubs acts as a bit of a proxy. So the message you saw in the Portal when performing the test send means it did successfully hand off the notification to FCM to be sent and got a success response back from FCM. This also means the device token was valid.
At that point there is no additional data Notification Hubs can provide since it is no longer in the system.
When we've seen issues like this in the past it tends to imply there is something not quite right in the app configuration itself. Likely there is some logic missing for handling the notification intent.
From the code snippet above, it looks like you are creating the installation from some server side piece and not from the application. My assumption is this means you are still working on integrating the Android SDK for Notification Hubs, since the normal flow is to have devices register themselves with the Hub.
We document the full end-to-end steps here: https://learn.microsoft.com/en-us/azure/notification-hubs/notification-hubs-android-push-notification-google-fcm-get-started
I would mostly recommend looking closely at the various code changes necessary on the Android side. Manifest updates and code changes. If you continue to have issues receiving notifications, please feel free to open a support request in the Portal and we can dig deeper into what's going on.
Looking at the docs for the SignalR bindings to send a message to a specified user you include the UserId property on the message as such -
[FunctionName("SendMessage")]
public static Task SendMessage(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")]object message,
[SignalR(HubName = "chat")]IAsyncCollector<SignalRMessage> signalRMessages)
{
return signalRMessages.AddAsync(
new SignalRMessage
{
// the message will only be sent to these user IDs
UserId = "userId1",
Target = "newMessage",
Arguments = new [] { message }
});
}
This example is taken straight from the documentation, but the comment implies you message multiple userids, even though the property is a string and not an array.
How would you specify multiple users? (If for example, they are in a private chat channel together) Or is this mistake in the wording of the comment and you would need to send a message per user?
With other versions of SignalR I would put them in a group, but bindings for this do not exist for functions.
Group operations were introduced in the latest release.
Now you can:
Send a message to a group using GroupName in SignalRMessage
Add/remove user in a group using IAsyncCollector<SignalRGroupAction> output
Unfortunately just like the doc says, right now with Azure function binding we can only send message to one user or to all clients.
See the code of current extension SDK Microsoft.Azure.WebJobs.Extensions.SignalRService v1.0.0-preview1-10002.
It shows the extension has only two methods SendToAll and SendToUser.
Task SendToAll(string hubName, SignalRData data);
Task SendToUser(string hubName, string userId, SignalRData data);
The comment confused you is actually for old sample, the author forgot to modify it.
Good news is that support for group operation is under progress.
I have used pusher recently in my PHP laravel project and it is working fine.
What I know about pusher is that it is a real time layer between our server and client and creates web socket connection to our client browser.
I setup pusher in my application using below tutorial:
pusher integration with laravel
What I have created using pusher for my web application:
1.I have created a notification functionality. Where when one user add some data to database say when one user starts following other user a event is triggered and that event sends data to particulr channel say 'notification-channel' and in my js code I have subscribed to this channel. For that I have written below line of codes:
//instantiate a Pusher object with our Credential's key
var pusher = new Pusher('68fd8888888888ee72c', {
encrypted: true
});
//Subscribe to the channel we specified in our Laravel Event
var channel = pusher.subscribe('notification-channel');
//Bind a function to a Event (the full Laravel class)
channel.bind('App\\Events\\HelloPusherEvent', addMessage);
By using addMessage() function I display some data. Now I have put a check on client side so that only if logged in user is intended to receive this message by writing simple if condition. As I have sent intended user's id in within data from App\Events\HelloPusherEvent so I used this Id to display msg to specific users only.
But I think this is not right approach to use pusher for notifications or any other functionality. Further in my project I want to use pusher for displaying new news feeds on user's newsfeed without page refresh, where obviously few users will see those posts according to whom is posting that news post.
But How I will user pusher in a way that I don't need to implement if conditions on client side to stop displaying data.
Here my concern is that if I will keep sending data to all the active clients and put if conditions to filter data that will ultimately degrade my application.
My concerns:
If pusher sends data to multiple clients that is obviously all the active users then will it not cause overhead.
Is there any option to use channels to channelize data to intended users only.
As I am implementing pusher first time I have few doubts regarding its actual working so is there any blog which can help me to understand its real time working.
Let me know if my question is not clear and specific enough, I will elaborate it further.
Thanks in advance to all who will try to answer.
This Question pusher-app-client-events explained that we can create different channels for different users to send msg to only intended users.
I go through this FAQ and came to know that we can create unlimited channels for one registered APP.
Creating multiple channels won't cause any overhead.
Now if I want to send notification to user 1 then I would create a channel 'notificaton-channel-1' and would subscribe user 1 to same channel within my frontend code.
The event class that I am using within my PHP laravel project looks like below:
<?php
namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
/**
* Just implement the ShouldBroadcast interface and Laravel will automatically
* send it to Pusher once we fire it
**/
class HelloPusherEvent extends Event implements ShouldBroadcast
{
use SerializesModels;
/**
* Only (!) Public members will be serialized to JSON and sent to Pusher
**/
public $message;
public $id;
public $for_user_id;
/**
* Create a new event instance.
* #param string $message (notification description)
* #param integer $id (notification id)
* #param integer $for_user_id (receiver's id)
* #author hkaur5
* #return void
*/
public function __construct($message,$id, $for_user_id)
{
$this->message = $message;
$this->id = $id;
$this->for_user_id = $for_user_id;
}
/**
* Get the channels the event should be broadcast on.
*
* #return array
*/
public function broadcastOn()
{
//We have created names of channel on basis of user's id who
//will receive data from this class.
//See frontend pusher code to see how we have used this channel
//for intended user.
return ['notification-channel_'.$this->for_user_id];
}
}
and on Frontend I subscribed to 'notification-channel-'+logged_in_user_id
//Subscribe to the channel we specified in our Laravel Event
//Subscribe user to the channel created for this user.
//For example if user's id is 1 then bind to notification-channel_1
var channel = pusher.subscribe('notification-channel_'+$('#logged_in_userId').val());
//Bind a function to a Event (the full Laravel class)
channel.bind('App\\Events\\HelloPusherEvent', addMessage);
This way we can send data to intended users only rather blocking data received by all the users by putting conditions in our client side code.
I think you should add User ID within Blade template directly, not using a form field:
var channel = pusher.subscribe('notification-channel_{{ Auth::id() }}');
I cant' get InterstitialAd to work in my UWP app when running on the Windows Phone Emulator (Note that I haven't tried it yet on actual real phone)
It works as expected when I run my UWP app in the Simulator or Local Machine as a Windows Store app.
Any ideas?
Thanks.
UPDATE - 1
Here is the code I use to display the InterstitialAd. In my MainPage.xaml.cs I have the following code:
public sealed partial class MainPage : Page
{
private InterstitialAd interstitialAd;
public MainPage()
{
this.InitializeComponent();
// Instantiate the interstitial video ad
interstitialAd = new InterstitialAd();
// Attach event handlers
interstitialAd.ErrorOccurred += OnAdError;
interstitialAd.AdReady += OnAdReady;
interstitialAd.Cancelled += OnAdCancelled;
interstitialAd.Completed += OnAdCompleted;
}
}
// This is an event handler for the interstitial ad. It is
// triggered when the interstitial ad is ready to play.
private void OnAdReady(object sender, object e)
{
// The ad is ready to show; show it.
interstitialAd.Show();
}
// This is an event handler for the interstitial ad. It is
// triggered when the interstitial ad is cancelled.
private void OnAdCancelled(object sender, object e)
{
}
// This is an event handler for the interstitial ad. It is
// triggered when the interstitial ad has completed playback.
private void OnAdCompleted(object sender, object e)
{
}
// This is an error handler for the interstitial ad.
private void OnAdError(object sender, AdErrorEventArgs e)
{
}
I've literarily taken this code from their UWP Store sample except that instead of launching this from a button, I'm launching it when my page is loaded:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Request an ad. When the ad is ready to show,
// the AdReady event will fire.
// The application id and ad unit id are passed in here.
// The application id and ad unit id can be obtained from Dev
// Center.
// See "Monetize with Ads" at https://msdn.microsoft.com/
// en-us/library/windows/apps/mt170658.aspx
#if DEBUG
interstitialAd.RequestAd(AdType.Video,
"d25517cb-12d4-4699-8bdc-52040c712cab", "11389925");
#else
interstitialAd.RequestAd(AdType.Video,
"d25517cb-12d4-4699-8bdc-52040c712cab", "11389925");
#endif
}
I've left both ApplicationId and UnitAdId as the test values for the time being as I haven't released it yet, well, not with ads anyway.
Thanks
UPDATE - 2:
I've added some debugging logs in the various events and this is what I get:
Page_Loaded: 00:00:00.0001590
Page_Loaded - RequestAd: 00:00:00.0091840
OnAdReady - Elasped Time: 00:01:04.6923865
OnAdError: NetworkConnectionFailure : Media timeout occurred
during playback. - Elasped
Time: 00:00:08.1955928
It takes over 1 minute for the OnAdReady to be triggered which is really odd and then I get an OnAdError 8 seconds later so from these logs you would assume that there is a network problem but my data is being loaded correctly which is all pulled from a web service, so there is definitely a connection. Also my AdMediator displays Ads as expected (well, kind of! That's another story!).
I will try to transfer it directly to my phone tomorrow and see if it makes any differences and I plug in my laptop to an Ethernet port instead of using the wireless but my wireless is pretty decent, so I'm not sure why I'm getting network connectivity errors.
The above code is correct and does actually work with UWP on Windows Phone but it doesn't work great in the emulator as:
it intermittently generates network connectivity errors when they are none.
it can take over a minute to display the advert but it eventually will.
I've just uploaded my app to the store and downloaded it to my phone and it works as expected. It displays the advert immediately or within a couple of seconds or so.
I would recommend you do not waste too much time trying it via the phone emulator as it appears to be the main problem. Implement your code and then test it directly on your device or download it from the store assuming you haven't released your app yet.
UPDATE:
I just thought I'd update my answer as I was just reading about the do's and dont's with interstitial adverts from UI and User Experience Guidelines and the way I'm actually doing it, is one of points in the "avoid" list.
It recommends that you fetch the advert 30-60 seconds ahead of time and you don't display it when the app starts, which is what I'm doing, so I guess I will change the logic in that regards.
I started to work with SignalR on Visual Studio 2012, so far I got the basic hang of it, I guided myself through this example (You can browse the code in the page). I decided I wanted to add a REST service to said example, so I added a basic service to it and it worked.
The next step I wanted to take was to add a communication between the service and SignalR, so according to what the example showed to me, I only needed to create a HubConnection through the url in my project (in this case, the example uses the url http:localhost:4200). You can check the WorkerRoleHubConfiguration class, it has a method that has the next line:
return RoleEnvironment.GetConfigurationSettingValue("GUI_URL");
Where GUI_URL is http:localhost:4200.
In my service class then I just added a method with the following:
var url = RoleEnvironment.GetConfigurationSettingValue("http://localhost:4200");
try
{
HubConnection _connection = new HubConnection(url);
IHubProxy _hub = _connection.CreateProxy("SiteMonitR");
_hub.Invoke("displayResult");
}
catch (Exception ex)
{
error = ex.ToString();
}
But that throws an exception, this one.
I don't undertand why I can get the url in the same way the example does, as I'm doing everything as it's done on the Server class.
The goal I'm trying to achieve is that when an endpoint is accesed and something changes in my system, SignalR notifies to the clients connected to it.
I hope anyone can help me understand what's wrong with my work.
EDIT
I'm adding my ServiceConfiguration.Local.cscfg, my ServiceConfiguration.Cloud.cscfg and ServiceDefinition.csdef files as reference here, I think the problem should be around there but to be honest I got no idea as to why this isn't working.
EDIT 2
I'm getting the following exception at this line var url = RoleEnvironment.GetConfigurationSettingValue("http://localhost:4200");
The exception is:
SEHExcetion occurred. External component has thrown an exception.
The URL is for a GUI - it has to be a web interface for signalr to negotiate the hub connection. In the example, the hub (server) sends updates to connections coming from the configured URL - again a web interface (html page).
The logic to communicate needs to reside in the Server class and be called from the worker role. For example, after making a call to your service in the worker role, then call server.DoSomething("message") to invoke a message to the server. THat code would look something like:
public Class Server
{ ...
public void DoSomething(string message)
{
_hub.Invoke("doSomething", message);
}
...
}
Then in Server.Run() add:
// whenever a DoSomething is called
_hub.On<string>("doSomething", (message) => _hub.Invoke("doSomething", message));
And in SiteMonitRNotificationHub
public class SiteMonitRNotificationHub : Hub
{
...
public void DoSomething(string address)
{
Clients.doingSomething(address);
}
...
}
Finally in the controller script in the web gui:
c.siteMonitorHub
...
.on('doingSomething', function (message) {
c.doSomething(message);
})
and...
this.doSomething= function (message) {
// do something in your web page with message
};