admin管理员组文章数量:1122832
My goal is to facilitate rpc between clients, where a client can be both the server and the client.
I have set on using the serverless Azure SignalR Service with an Azure Function for trigger binding.
The issue that I face right now is that I can't invoke those methods from the client. The SignalR trigger broadcast is not invoked.
My question is if I approach the problem right, I want to send messages to the SignalR Service, from which the azure function reacts to the connected listeners, but when connecting straight to the service i get 403 no matter what (I do not have managed identity turned on), do I have to mediate it via the function?
I have created a serverless Azure SignalR Service and an Azure Function.
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker.SignalRService;
using Microsoft.Extensions.Logging;
namespace DotnetIsolated_ClassBased
{
[SignalRConnection("AzureSignalRConnectionString")]
public class Functions : ServerlessHub<IChatClient>
{
private const string HubName = "<hub_name>"
private readonly ILogger _logger;
public Functions(IServiceProvider serviceProvider, ILogger<Functions> logger) : base(serviceProvider)
{
_logger = logger;
}
[Function(nameof(BroadcastToAll))]
[SignalROutput(HubName = HubName, ConnectionStringSetting = "SignalRConnection")]
public static SignalRMessageAction BroadcastToAll([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
using var bodyReader = new StreamReader(req.Body);
return new SignalRMessageAction("newMessage")
{
// broadcast to all the connected clients without specifying any connection, user or group.
Arguments = new[] { bodyReader.ReadToEnd() },
};
}
[Function("negotiate")]
public async Task<HttpResponseData> Negotiate([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
_logger.LogInformation($"Negotiating with user: {req.Headers.GetValues("userId").FirstOrDefault()}");
var negotiateResponse = await NegotiateAsync(new() { UserId = req.Headers.GetValues("userId").FirstOrDefault() });
var response = req.CreateResponse();
await response.WriteBytesAsync(negotiateResponse.ToArray());
return response;
}
[Function("OnConnected")]
public Task OnConnected([SignalRTrigger(HubName, "connections", "connected")] SignalRInvocationContext invocationContext)
{
invocationContext.Headers.TryGetValue("Authorization", out var auth);
_logger.LogInformation($"{invocationContext.ConnectionId} has connected");
return Clients.All.newConnection(new NewConnection(invocationContext.ConnectionId, auth));
}
[Function("Broadcast")]
public Task Broadcast(
[SignalRTrigger(HubName, "messages", "broadcast", "message")] SignalRInvocationContext invocationContext, string message)
{
_logger.LogInformation($"Broadcasting message: {message}");
return Clients.All.newMessage(new NewMessage(invocationContext, message));
}
[Function("SendToGroup")]
public Task SendToGroup([SignalRTrigger(HubName, "messages", "SendToGroup", "groupName", "message")] SignalRInvocationContext invocationContext, string groupName, string message)
{
return Clients.Group(groupName).newMessage(new NewMessage(invocationContext, message));
}
[Function("SendToUser")]
public Task SendToUser([SignalRTrigger(HubName, "messages", "SendToUser", "userName", "message")] SignalRInvocationContext invocationContext, string userName, string message)
{
return Clients.User(userName).newMessage(new NewMessage(invocationContext, message));
}
[Function("SendToConnection")]
public Task SendToConnection([SignalRTrigger(HubName, "messages", "SendToConnection", "connectionId", "message")] SignalRInvocationContext invocationContext, string connectionId, string message)
{
return Clients.Client(connectionId).newMessage(new NewMessage(invocationContext, message));
}
[Function("JoinGroup")]
public Task JoinGroup([SignalRTrigger(HubName, "messages", "JoinGroup", "connectionId", "groupName")] SignalRInvocationContext invocationContext, string connectionId, string groupName)
{
return Groups.AddToGroupAsync(connectionId, groupName);
}
[Function("LeaveGroup")]
public Task LeaveGroup([SignalRTrigger(HubName, "messages", "LeaveGroup", "connectionId", "groupName")] SignalRInvocationContext invocationContext, string connectionId, string groupName)
{
return Groups.RemoveFromGroupAsync(connectionId, groupName);
}
[Function("JoinUserToGroup")]
public Task JoinUserToGroup([SignalRTrigger(HubName, "messages", "JoinUserToGroup", "userName", "groupName")] SignalRInvocationContext invocationContext, string userName, string groupName)
{
return UserGroups.AddToGroupAsync(userName, groupName);
}
[Function("LeaveUserFromGroup")]
public Task LeaveUserFromGroup([SignalRTrigger(HubName, "messages", "LeaveUserFromGroup", "userName", "groupName")] SignalRInvocationContext invocationContext, string userName, string groupName)
{
return UserGroups.RemoveFromGroupAsync(userName, groupName);
}
[Function("OnDisconnected")]
public void OnDisconnected([SignalRTrigger(HubName, "connections", "disconnected")] SignalRInvocationContext invocationContext)
{
_logger.LogInformation($"{invocationContext.ConnectionId} has disconnected");
}
}
public class NewConnection
{
public string ConnectionId { get; }
public string Authentication { get; }
public NewConnection(string connectionId, string auth)
{
ConnectionId = connectionId;
Authentication = auth;
}
}
public class NewMessage
{
public string ConnectionId { get; }
public string Sender { get; }
public string Text { get; }
public NewMessage(SignalRInvocationContext invocationContext, string message)
{
Sender = string.IsNullOrEmpty(invocationContext.UserId) ? string.Empty : invocationContext.UserId;
ConnectionId = invocationContext.ConnectionId;
Text = message;
}
}
public interface IChatClient
{
Task newMessage(NewMessage message);
Task newConnection(NewConnection connection);
}
// both clients look like this
var connection = new HubConnectionBuilder()
.WithUrl("<azure_function_endpoint>/api", (options) =>
{
options.Headers.Add("UserId", "<some_user_id>");
options.AccessTokenProvider = async () =>
{
return await Task.FromResult("<access_token>"); // ikn about hardcoing tokens, this is just for the poc
};
})
.Build();
connection.On<string>("Broadcast", Console.WriteLine);
await connection.StartAsync();
await connection.SendAsync("Broadcast", "This is a broadcast");
My goal is to facilitate rpc between clients, where a client can be both the server and the client.
I have set on using the serverless Azure SignalR Service with an Azure Function for trigger binding.
The issue that I face right now is that I can't invoke those methods from the client. The SignalR trigger broadcast is not invoked.
My question is if I approach the problem right, I want to send messages to the SignalR Service, from which the azure function reacts to the connected listeners, but when connecting straight to the service i get 403 no matter what (I do not have managed identity turned on), do I have to mediate it via the function?
I have created a serverless Azure SignalR Service and an Azure Function.
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker.SignalRService;
using Microsoft.Extensions.Logging;
namespace DotnetIsolated_ClassBased
{
[SignalRConnection("AzureSignalRConnectionString")]
public class Functions : ServerlessHub<IChatClient>
{
private const string HubName = "<hub_name>"
private readonly ILogger _logger;
public Functions(IServiceProvider serviceProvider, ILogger<Functions> logger) : base(serviceProvider)
{
_logger = logger;
}
[Function(nameof(BroadcastToAll))]
[SignalROutput(HubName = HubName, ConnectionStringSetting = "SignalRConnection")]
public static SignalRMessageAction BroadcastToAll([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
using var bodyReader = new StreamReader(req.Body);
return new SignalRMessageAction("newMessage")
{
// broadcast to all the connected clients without specifying any connection, user or group.
Arguments = new[] { bodyReader.ReadToEnd() },
};
}
[Function("negotiate")]
public async Task<HttpResponseData> Negotiate([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
_logger.LogInformation($"Negotiating with user: {req.Headers.GetValues("userId").FirstOrDefault()}");
var negotiateResponse = await NegotiateAsync(new() { UserId = req.Headers.GetValues("userId").FirstOrDefault() });
var response = req.CreateResponse();
await response.WriteBytesAsync(negotiateResponse.ToArray());
return response;
}
[Function("OnConnected")]
public Task OnConnected([SignalRTrigger(HubName, "connections", "connected")] SignalRInvocationContext invocationContext)
{
invocationContext.Headers.TryGetValue("Authorization", out var auth);
_logger.LogInformation($"{invocationContext.ConnectionId} has connected");
return Clients.All.newConnection(new NewConnection(invocationContext.ConnectionId, auth));
}
[Function("Broadcast")]
public Task Broadcast(
[SignalRTrigger(HubName, "messages", "broadcast", "message")] SignalRInvocationContext invocationContext, string message)
{
_logger.LogInformation($"Broadcasting message: {message}");
return Clients.All.newMessage(new NewMessage(invocationContext, message));
}
[Function("SendToGroup")]
public Task SendToGroup([SignalRTrigger(HubName, "messages", "SendToGroup", "groupName", "message")] SignalRInvocationContext invocationContext, string groupName, string message)
{
return Clients.Group(groupName).newMessage(new NewMessage(invocationContext, message));
}
[Function("SendToUser")]
public Task SendToUser([SignalRTrigger(HubName, "messages", "SendToUser", "userName", "message")] SignalRInvocationContext invocationContext, string userName, string message)
{
return Clients.User(userName).newMessage(new NewMessage(invocationContext, message));
}
[Function("SendToConnection")]
public Task SendToConnection([SignalRTrigger(HubName, "messages", "SendToConnection", "connectionId", "message")] SignalRInvocationContext invocationContext, string connectionId, string message)
{
return Clients.Client(connectionId).newMessage(new NewMessage(invocationContext, message));
}
[Function("JoinGroup")]
public Task JoinGroup([SignalRTrigger(HubName, "messages", "JoinGroup", "connectionId", "groupName")] SignalRInvocationContext invocationContext, string connectionId, string groupName)
{
return Groups.AddToGroupAsync(connectionId, groupName);
}
[Function("LeaveGroup")]
public Task LeaveGroup([SignalRTrigger(HubName, "messages", "LeaveGroup", "connectionId", "groupName")] SignalRInvocationContext invocationContext, string connectionId, string groupName)
{
return Groups.RemoveFromGroupAsync(connectionId, groupName);
}
[Function("JoinUserToGroup")]
public Task JoinUserToGroup([SignalRTrigger(HubName, "messages", "JoinUserToGroup", "userName", "groupName")] SignalRInvocationContext invocationContext, string userName, string groupName)
{
return UserGroups.AddToGroupAsync(userName, groupName);
}
[Function("LeaveUserFromGroup")]
public Task LeaveUserFromGroup([SignalRTrigger(HubName, "messages", "LeaveUserFromGroup", "userName", "groupName")] SignalRInvocationContext invocationContext, string userName, string groupName)
{
return UserGroups.RemoveFromGroupAsync(userName, groupName);
}
[Function("OnDisconnected")]
public void OnDisconnected([SignalRTrigger(HubName, "connections", "disconnected")] SignalRInvocationContext invocationContext)
{
_logger.LogInformation($"{invocationContext.ConnectionId} has disconnected");
}
}
public class NewConnection
{
public string ConnectionId { get; }
public string Authentication { get; }
public NewConnection(string connectionId, string auth)
{
ConnectionId = connectionId;
Authentication = auth;
}
}
public class NewMessage
{
public string ConnectionId { get; }
public string Sender { get; }
public string Text { get; }
public NewMessage(SignalRInvocationContext invocationContext, string message)
{
Sender = string.IsNullOrEmpty(invocationContext.UserId) ? string.Empty : invocationContext.UserId;
ConnectionId = invocationContext.ConnectionId;
Text = message;
}
}
public interface IChatClient
{
Task newMessage(NewMessage message);
Task newConnection(NewConnection connection);
}
// both clients look like this
var connection = new HubConnectionBuilder()
.WithUrl("<azure_function_endpoint>/api", (options) =>
{
options.Headers.Add("UserId", "<some_user_id>");
options.AccessTokenProvider = async () =>
{
return await Task.FromResult("<access_token>"); // ikn about hardcoing tokens, this is just for the poc
};
})
.Build();
connection.On<string>("Broadcast", Console.WriteLine);
await connection.StartAsync();
await connection.SendAsync("Broadcast", "This is a broadcast");
Share
Improve this question
asked Nov 21, 2024 at 11:45
Pultea RobertPultea Robert
1
2
- Are you using this code locally or in function app? – Ikhtesam Afrin Commented Nov 22, 2024 at 3:37
- In the function app. For the upstream setting I have set my azure function endpoint in the signalr service. – Pultea Robert Commented Nov 22, 2024 at 6:27
2 Answers
Reset to default 0I want to send messages to the SignalR Service, from which the azure function reacts to the connected listeners, but when connecting straight to the service i get 403 no matter what
- It looks like a permission issue, make sure you have added the SignalR connection string in the Environment Variables of function app.
- Verify, if you have constructed the Upstream in correct format like {functionAppURL}/runtime/webhooks/signalr?code={signalrExtensionCode}.
- I am using the same code alike you and also have added below code in program.cs file.
using DotnetIsolated_ClassBased;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = FunctionsApplication.CreateBuilder(args);
builder.ConfigureFunctionsWebApplication();
builder.Services.Configure<KestrelServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
builder.Services.AddServerlessHub<Function1>();
builder.Build().Run();
- I have added the function app Url and access token in client code and below functions have executed successfully.
- You can also visualize the Live SignalR logs.
just an update, I have managed to make it work by completely creating another resource group and then the function, there seems to be an issue with the resource group as it was made years ago. One other thing that I want to mention is that the client must match the type if you are using strong typed hubs, return Clients.All.newMessage(new NewMessage(invocationContext, message));
on the client side would look like connection.On<NewMessage>("Broadcast", (value) => Console.WriteLine(Serialize(value)));
本文标签: cSending messages to the serverless Azure SignalR ServiceStack Overflow
版权声明:本文标题:c# - Sending messages to the serverless Azure SignalR Service - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736311251a1934670.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论