I am unable to resolve why my Azure SignalR Hub method is not being triggered?
Environment: Xamarin.Forms client
Disclaimer:
My LocationHub class is in a separate project that is referenced by the project that hosts my Azure Function.
Can Azure SignalR invoke a hub method that's in a separate library?
Server: Here's the hub class:
type LocationHub() =
inherit Hub()
member x.SendLocation(v:SubjectLocation) =
async { do! (x :> Hub).Clients.All.SendAsync($"{v.SessionId}", v) |> Async.AwaitTask } |> Async.StartAsTask
Server: Here's the Azure function that is suppose to trigger the method on the hub class:
public static class LocationFn
{
[FunctionName(nameof(LocationFn))]
public static async Task<IActionResult> Run(
[HttpTrigger(
AuthorizationLevel.Anonymous,
"post",
Route = "locationfn")]
HttpRequest req,
[SignalR(HubName = "LocationHub")]
IAsyncCollector<SignalRMessage> signalRMessages,
ILogger log)
{
log.LogInformation($"{nameof(LocationFn)} has been invoked.");
try
{
using (var streamReader = new StreamReader(req.Body))
{
var json = await streamReader.ReadToEndAsync();
var subjectLocation = JsonConvert.DeserializeObject<SubjectLocation>(json);
await signalRMessages.AddAsync(
new SignalRMessage
{
Target = "SendLocation",
Arguments = new[] { subjectLocation }
});
var message = Log(log, subjectLocation);
return new OkObjectResult(message);
}
}
catch (Exception ex)
{
return new BadRequestObjectResult("There was an error: " + ex.Message);
}
}
static string Log(ILogger log, SubjectLocation subjectLocation)
{
var location = subjectLocation.Location;
var latitude = location.Latitude;
var longitude = location.Longitude;
var message = $"Received location: {subjectLocation.SubjectId} at '({latitude},{longitude})'";
log.LogInformation($"{nameof(LocationFn)} {message}");
return message;
}
}
Appendix:
Client: I have the following client request:
var sessionId = "some_session_id";
await CourierTracking.connectOn(sessionId, locationTracking(), "negotiatefn");
Client: The bird's-eye view of establishing a connection is implemented here:
open System.Diagnostics
open OrderRequest.SignalR.Client
module CourierTracking =
let private onConnectionChanged (_,_) = ()
let private onMessageReceived msg = Debug.WriteLine(sprintf "%A" msg)
let private signalR = SignalRService();
let connectOn(sessionId:string) (serviceHost:string) (resourceName:string) =
signalR.Connected .Add onConnectionChanged
signalR.ConnectionFailed .Add onConnectionChanged
signalR.MessageReceived .Add onMessageReceived
async {
do! signalR.ConnectOn(serviceHost, resourceName, sessionId) |> Async.AwaitTask
} |> Async.StartAsTask
Client: Here's the core implementation for connecting and receiving messages:
public class SignalRService
{
HttpClient _client = new HttpClient();
public delegate void MessageReceivedHandler(object sender, CourierLocation message);
public delegate void ConnectionHandler(object sender, bool successful, string message);
public event MessageReceivedHandler MessageReceived;
public event ConnectionHandler Connected;
public event ConnectionHandler ConnectionFailed;
public bool IsConnected { get; private set; }
public bool IsBusy { get; private set; }
public async Task ConnectOn(string host, string nameOfNegotiationFn, string sessionId)
{
try
{
IsBusy = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var negotiateJson = await _client.GetStringAsync($"{host}{nameOfNegotiationFn}");
var negotiate = JsonConvert.DeserializeObject<NegotiateInfo>(negotiateJson);
var connection = new HubConnectionBuilder()
.AddNewtonsoftJsonProtocol()
.WithUrl(negotiate.Url, options => options.AccessTokenProvider = async () => negotiate.AccessToken)
.Build();
connection.Closed += Connection_Closed;
connection.On<JObject>(sessionId, OnIncomingMessage);
await connection.StartAsync();
IsConnected = true;
IsBusy = false;
Connected?.Invoke(this, true, "Connection successful.");
}
catch (Exception ex)
{
ConnectionFailed?.Invoke(this, false, ex.Message);
IsConnected = false;
IsBusy = false;
}
}
Task Connection_Closed(Exception arg)
{
ConnectionFailed?.Invoke(this, false, arg.Message);
IsConnected = false;
IsBusy = false;
return Task.CompletedTask;
}
void OnIncomingMessage(JObject message)
{
var courierId = message.GetValue("SubjectId").ToString();
var location = message.SelectToken("Location");
var latitude = double.Parse(location.SelectToken("Latitude").ToString());
var longitude = double.Parse(location.SelectToken("Longitude").ToString());
var courierLocation = new CourierLocation(courierId, new Coordinate(latitude, longitude));
MessageReceived?.Invoke(this, courierLocation);
}
}