4
votes

I have created an Azure SignalR service in serverless mode. I have a suite of functions in an Azure Function App to manage a chat service which must include groups. When publishing the Function App I set the AzureSignalRConnectionString to that set under the Keys setting in the SignalR service. I have double checked in the Azure Portal that the connectionstring is present and correct.

I can use Postman to call the individual functions in Azure successfully and can monitor this in the Azure Portal and see logging messages etc.

However, if I use HubConnection in the client to attempt to call a function, it does not get there. Also, messages sent successfully by Postman do not cause the hub to fire receipt.

So the following call using the REST interface can be seen to execute from the Azure Portal:

await client.PostAsync($"{baseUrl}/api/{group}/add/{userName}", new StringContent(string.Empty));

however the following call using HubConnection does not trigger the same Azure Function:

await hubConnection.SendAsync("AddToGroup", group, userName);

The function bindings for this are:

[FunctionName("AddToGroup")]
public static async Task<IActionResult> Run(
    [HttpTrigger(
    AuthorizationLevel.Anonymous, 
    "post", 
    Route = "{groupName}/add/{userId}")] 
    HttpRequest req,
    string groupName,
    string userId,
    [SignalR(HubName = Constants.HubName)]
    IAsyncCollector<SignalRGroupAction> signalRGroupActions,
    ILogger log)
{
    log.LogInformation("AddToGroup invoked");

    await signalRGroupActions.AddAsync(
    new SignalRGroupAction
    {
        UserId = userId,
        GroupName = groupName,
        Action = GroupAction.Add
    });

    return new OkObjectResult("All done");
}

The Function App contains a Negotiate function which is used to initialise the Hub Connection like this:

var negotiateJson = await client.GetStringAsync($"{baseUrl}/api/negotiate");
var info = JsonConvert.DeserializeObject<NegotiateInfo>(negotiateJson);
hubConnection = new HubConnectionBuilder()
    .WithUrl(info.Url, options =>
    {
        options.AccessTokenProvider = () => Task.FromResult(info.AccessToken);
    })
    .Build();

Debugging this shows the Url to be correct and the Access Token is present.

I am using Microsoft.AspNetCore.SignalR.Client V3.0.0 and have followed through a number of guides and samples but cannot get the Azure SignalR hub to work.

(I have worked with hosted SignalR in the past and found it pretty straight forward but have not worked with the Azure service or Azure Functions before).

I would be eternally grateful for any assistance or guidance.

Thank you

2
The access token from /negotiate needs to contain the user id. That will authenticate the SignalR connection to that user and allow the group actions to work. How you do this depends on what is a "user" in your app and how you authenticate them.Anthony Chu
Thank you Anthony. I don't understand how you would get the access token to contain a userId. I do have a userId and could include it as a parameter to Negotiate but I have no idea how that could then be used when generating the access token.user3687273
I'd also be interested in this @AnthonyChu , can you expand please?ThrowingSpoon

2 Answers

1
votes

Azure SignalR Service + Azure Functions currently doesn't support sending messages from a client to the service via SignalR. The same is documented as well.

Instead, you will have to directly make the HTTP requests from the client.

Also, if required, you can achieve what you are trying by using ASP.NET Core with Azure SignalR instead of Azure Functions.

1
votes

Azure SignalR Service + Azure Functions supports sending messages from a client to service via SignalR (with ASP.NET CORE)

Serverside (Traditional Model):

[FunctionName("notifyServerViaSignalR")]
public async Task NotifyServerViaSignalR([SignalRTrigger("YourHub", "messages", "NotifyServerViaSignalR")]
InvocationContext invocationContext, [SignalRParameter] string additionalParameter, ILogger logger)
{
    logger.LogInformation($"Processed NotifyServerViaSignalR - AdditionalParameter:{additionalParameter} from ConnectionId:{invocationContext.ConnectionId}.");
}

Also configure, Upstream URL in Azure SignalR settings

<Function_App_URL>/runtime/webhooks/signalr?code=<API_KEY>

The Function_App_URL can be found on Function App's Overview page and The API_KEY is generated by Azure Function. You can get the API_KEY from signalr_extension in the App keys blade of Function App.

Clientside:

connection.invoke("NotifyServerViaSignalR", additionalParameter);