5

I'm using an Azure Function to read messages off an Event Hub and post them to a SignalR instance. I have frontend JavaScript that is connected to the SignalR instance and is receiving messages. What I want is to dynamically set the "HubName" attribute in my Azure Function base off a value passed in. Is this poissble? I have included my Azure Function calls below. In both methods, I would like to dynamically set the HubName value, which is hard coded to 'deviceMessages'

    public static SignalRConnectionInfo GetSignalRInfo(
        [HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req,
        [SignalRConnectionInfo(HubName = "deviceMessages")] SignalRConnectionInfo connectionInfo)
    {
        return connectionInfo;
    }


    public static Task SendMessage(
        [EventHubTrigger("{EventHubName}", Connection = "EventHubConnectionAppSetting")]string myEventHubMessage,
        [SignalR(HubName = "deviceMessages")] IAsyncCollector<SignalRMessage> signalRMessages)
    {
        return signalRMessages.AddAsync(
            new SignalRMessage
            {
                Target = "newMessage",
                Arguments = new[] { myEventHubMessage }
            });
    }
jvencl
  • 143
  • 8

2 Answers2

7

You can do binding the imperative way. Use IBinder binder in the signature of your function and you can create bindings at runtime.

var signalRAttribute = new SignalRAttribute(/* your settings here */));
var outputMessages = await binder.BindAsync<IAsyncCollector<SignalRMessage>>(signalRAttribute);

There are also good samples for this here:

How do I use Binder to perform dynamic bindings in my C# Function?

and here:

https://weblogs.asp.net/sfeldman/azure-functions-to-make-audit-queue-and-auditors-happy

Sebastian Achatz
  • 668
  • 4
  • 14
4

I found a separate way around this issue, by using the UserId property of the SignalRConnectionInfo object. I pass the id of the device I want to receive messages for in the 'negotiate' call using a custom header field, which returns a token for that id. I then set that value in the SignalRMessage object when a message is received from the Event Hub. This way, the device page that I'm on is only receiving message that that particular device is sending.

    [FunctionName("negotiate")]
    public static SignalRConnectionInfo GetSignalRInfo(
        [HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req,
        [SignalRConnectionInfo(HubName = "deviceMessages", UserId = "{headers.deviceId}")] SignalRConnectionInfo connectionInfo)
    {
        return connectionInfo;
    }

    [FunctionName("messages")]
    public static Task SendMessage(
        [EventHubTrigger("{EventHubName}", Connection = "EventHubConnectionAppSetting")]string myEventHubMessage,
        [SignalR(HubName = "deviceMessages")] IAsyncCollector<SignalRMessage> signalRMessages)
    {
        var dev = JToken.Parse(Convert.ToString(myEventHubMessage));
        DeviceMessage msg = dev.ToObject<DeviceMessage>();

        return signalRMessages.AddAsync(
            new SignalRMessage
            {
                UserId = msg.deviceId,
                Target = "newMessage",
                Arguments = new[] { myEventHubMessage }
            });
    }
jvencl
  • 143
  • 8
  • what type of client are you using on the client side? I'm attempting the same thing and the JavaScript client does not allow this (would have to possibly intercept at XHR level to inject). If JavaScript, what solution did you use to inject the header? – Charles Chen Sep 03 '19 at 01:01
  • @CharlesChen did you find a solution? – Anthony Ryan May 11 '20 at 19:26
  • 1
    @AnthonyRyan: I wrote a post about this here: https://charliedigital.com/2019/09/02/azure-functions-signalr-and-authorization/ The gist of it is that rather than using the declarative attribute-based bindings, it is possible to use BindAsync. – Charles Chen May 12 '20 at 20:22