I have a function that is used to store data. This data is monitored in a frontend and when there is new data, I want to notify my frontend about that. I have achieved that by using SignalR, which to work requires me to return SignalRMessageAction from my function instead of typical HttpResponseData.
This is functioning, however there are some problems and questions that I have.
I have error-handling middleware that is configured to catch all of my custom exception, however when I throw an exception from a function that returns SignalRMessageAction, it is not caught and my server return 500 instead of meaningful exception.
Imagine that I want to have GET request that when triggered, will also notify subscribers and will return requested data and not the SignalRMessageAction.
Here is simplified example how I use it now.
[Function("StoreGeofenceMessage")]
[SignalROutput(HubName = "myhub")] //Hub name is not actually used anywhere
public async Task<SignalRMessageAction> RunStoreMessage(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "v1/messages")] HttpRequestData req,
FunctionContext executionContext)
{
var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
MessageType messageType = data.type;;
int geofenceId = data.geofenceId;
DateTime timestamp = data.timestamp;
var geofenceMessage = new GeofenceMessage(geofenceId, timestamp, messageType);
messagesService.StoreMessage(geofenceMessage);
return new SignalRMessageAction("dashboardUpdate")
{
Arguments = new object[] { }
};
}
This is the Negotiate function
[Function("Negotiate")]
public SignalRConnectionInfo Negotiate(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "v1/negotiate")]
HttpRequestData req,
[SignalRConnectionInfoInput(HubName = "myhub")]
SignalRConnectionInfo connectionInfo)
{
return connectionInfo;
}
I have tried to search for more information, but couldn't find something useful. I expect that I will have control over the response from my server and will be able to notify my subscribers in some other form.
I have managed to solve my problem with a little bit more elegant solution that the one provided by Sampath (which I think that also should work).
I have used ServiceHubContext to send messages instead of the output binding, so I have made this service:
public class SignalRService: IHostedService
{
public ServiceHubContext MessageHubContext { get; private set; }
private const string MESSAGE_HUB = "myhub";
private readonly IConfiguration configuration;
private readonly ILoggerFactory loggerFactory;
public SignalRService(IConfiguration configuration, ILoggerFactory
loggerFactory)
{
this.configuration = configuration;
this.loggerFactory = loggerFactory;
}
async Task IHostedService.StartAsync(CancellationToken cancellationToken)
{
using var serviceManager = new ServiceManagerBuilder()
.WithOptions(o => o.ConnectionString =
Environment.GetEnvironmentVariable("AzureSignalRConnectionString"))
.WithLoggerFactory(loggerFactory)
.BuildServiceManager();
MessageHubContext = await
serviceManager.CreateHubContextAsync(MESSAGE_HUB, cancellationToken);
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public async Task SendMessage(string target)
{
await MessageHubContext.Clients.All.SendAsync(target);
}
}
and then just inject that service like:
s.AddSingleton<SignalRService>();
s.AddHostedService(sp => sp.GetRequiredService<SignalRService>());
Credits to hannes neukermans in this post.