问题描述
我一直在关注 SignalR Bidirectional sample 中提供的示例,但似乎无法从 SendToGroup 示例中获得响应。当我在本地运行 Azure 函数时,我可以看到协商成功发生并且令牌被发回,但对 Azure 函数的其他调用都无法从索引页面工作。
函数.cs
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Extensions.Signalrservice;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Mvc;
namespace FunctionApp
{
public class SimpleChat : ServerlessHub
{
private const string NewMessageTarget = "newMessage";
private const string NewConnectionTarget = "newConnection";
[FunctionName("index")]
public IActionResult GetHomePage([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req,ExecutionContext context)
{
var path = Path.Combine(context.FunctionAppDirectory,"content","index.html");
Console.WriteLine(path);
return new ContentResult
{
Content = File.ReadAllText(path),ContentType = "text/html",};
}
[FunctionName("negotiate")]
public SignalRConnectionInfo Negotiate([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req)
{
return Negotiate(req.Headers["x-ms-signalr-user-id"],GetClaims(req.Headers["Authorization"]));
}
[FunctionName(nameof(OnConnected))]
public async Task OnConnected([SignalRTrigger]InvocationContext invocationContext,ILogger logger)
{
invocationContext.Headers.TryGetValue("Authorization",out var auth);
await Clients.All.SendAsync(NewConnectionTarget,new NewConnection(invocationContext.ConnectionId,auth));
logger.Loginformation($"{invocationContext.ConnectionId} has connected");
}
[FunctionAuthorize]
[FunctionName(nameof(broadcast))]
public async Task broadcast([SignalRTrigger]InvocationContext invocationContext,string message,ILogger logger)
{
await Clients.All.SendAsync(NewMessageTarget,new NewMessage(invocationContext,message));
logger.Loginformation($"{invocationContext.ConnectionId} broadcast {message}");
}
[FunctionName(nameof(SendToGroup))]
public async Task SendToGroup([SignalRTrigger]InvocationContext invocationContext,string groupName,string message)
{
await Clients.Group(groupName).SendAsync(NewMessageTarget,message));
}
[FunctionName(nameof(SendToUser))]
public async Task SendToUser([SignalRTrigger]InvocationContext invocationContext,string userName,string message)
{
await Clients.User(userName).SendAsync(NewMessageTarget,message));
}
[FunctionName(nameof(SendToConnection))]
public async Task SendToConnection([SignalRTrigger]InvocationContext invocationContext,string connectionId,string message)
{
await Clients.Client(connectionId).SendAsync(NewMessageTarget,message));
}
[FunctionName(nameof(JoinGroup))]
public async Task JoinGroup([SignalRTrigger]InvocationContext invocationContext,string groupName)
{
await Groups.AddToGroupAsync(connectionId,groupName);
}
[FunctionName(nameof(LeaveGroup))]
public async Task LeaveGroup([SignalRTrigger]InvocationContext invocationContext,string groupName)
{
await Groups.RemoveFromGroupAsync(connectionId,groupName);
}
[FunctionName(nameof(JoinUserToGroup))]
public async Task JoinUserToGroup([SignalRTrigger]InvocationContext invocationContext,string groupName)
{
await UserGroups.AddToGroupAsync(userName,groupName);
}
[FunctionName(nameof(LeaveUserFromGroup))]
public async Task LeaveUserFromGroup([SignalRTrigger]InvocationContext invocationContext,string groupName)
{
await UserGroups.RemoveFromGroupAsync(userName,groupName);
}
[FunctionName(nameof(Ondisconnected))]
public void Ondisconnected([SignalRTrigger]InvocationContext invocationContext)
{
}
private class NewConnection
{
public string ConnectionId { get; }
public string Authentication { get; }
public NewConnection(string connectionId,string authentication)
{
ConnectionId = connectionId;
Authentication = authentication;
}
}
private class NewMessage
{
public string ConnectionId { get; }
public string Sender { get; }
public string Text { get; }
public NewMessage(InvocationContext invocationContext,string message)
{
Sender = string.IsNullOrEmpty(invocationContext.UserId) ? string.Empty : invocationContext.UserId;
ConnectionId = invocationContext.ConnectionId;
Text = message;
}
}
}
}
网络客户端
<html>
<head>
<title>Serverless Chat</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/css/bootstrap.min.css">
<script>
window.apiBaseUrl = window.location.origin;
</script>
<style>
.slide-fade-enter-active,.slide-fade-leave-active {
transition: all 1s ease;
}
.slide-fade-enter,.slide-fade-leave-to {
height: 0px;
overflow-y: hidden;
opacity: 0;
}
</style>
</head>
<body>
<p> </p>
<div id="app" class="container">
<h3>Serverless chat</h3>
<div class="row" v-if="ready">
<div class="signalr-demo col-sm">
<hr />
<div id='groupchecked'>
<input type="checkBox" id="checkBox" v-model="checked">
<label for="checkBox">Send To Default Group: {{ this.defaultgroup }}</label>
</div>
<form v-on:submit.prevent="sendNewMessage(checked)">
<input type="text" v-model="newMessage" id="message-Box" class="form-control" placeholder="Type message here..." />
</form>
</div>
</div>
<div class="row" v-if="!ready">
<div class="col-sm">
<div>Loading...</div>
</div>
</div>
<div v-if="ready">
<transition-group name="slide-fade" tag="div">
<div class="row" v-for="message in messages" v-bind:key="message.id">
<div class="col-sm">
<hr />
<div>
<div style="display: inline-block; padding-left: 12px;">
<div>
<a href="#" v-on:click.prevent="sendPrivateMessage(message.Sender)">
<span class="text-info small">
<strong>{{ message.Sender || message.sender }}</strong>
</span>
</a>
<span v-if="message.ConnectionId || message.connectionId">
<a href="#" v-on:click.prevent="sendToConnection(message.ConnectionId || message.connectionId)">
<span class="badge badge-primary">Connection: {{ message.ConnectionId || message.connectionId }}</span>
</a>
</span>
<a href="#" v-on:click.prevent="addUserToGroup(message.Sender || message.sender)">
<span class="badge badge-primary">AddUserToGroup</span>
</a>
<a href="#" v-on:click.prevent="removeUserFromGroup(message.Sender || message.sender)">
<span class="badge badge-primary">RemoveUserFromGroup</span>
</a>
<a href="#" v-on:click.prevent="addConnectionToGroup(message.ConnectionId || message.connectionId)">
<span v-if="message.ConnectionId || message.connectionId" class="badge badge-primary">AddConnectionToGroup</span>
</a>
<a href="#" v-on:click.prevent="removeConnectionIdFromGroup(message.ConnectionId || message.connectionId)">
<span v-if="message.ConnectionId || message.connectionId" class="badge badge-primary">RemoveConnectionFromGroup</span>
</a>
<span v-if="message.IsPrivate || message.isPrivate" class="badge badge-secondary">private message
</span>
</div>
<div>
{{ message.Text || message.text }}
</div>
</div>
</div>
</div>
</div>
</transition-group>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@aspnet/signalr@1.0.3/dist/browser/signalr.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/crypto-js@3.1.9-1/crypto-js.js"></script>
<script src="https://cdn.jsdelivr.net/npm/crypto-js@3.1.9-1/enc-base64.js"></script>
<script>
const data = {
username: '',defaultgroup: 'AzureSignalR',checked: false,newMessage: '',messages: [],myConnectionId: '',ready: false
};
const app = new Vue({
el: '#app',data: data,methods: {
sendNewMessage: function (isToGroup) {
if (isToGroup) {
connection.invoke("sendToGroup",this.defaultgroup,this.newMessage);
}
else {
connection.invoke("broadcast",this.newMessage);
}
this.newMessage = '';
},sendPrivateMessage: function (user) {
const messageText = prompt('Send private message to ' + user);
if (messageText) {
connection.invoke("sendToUser",user,messageText);
}
},sendToConnection: function (connectionId) {
const messageText = prompt('Send private message to connection ' + connectionId);
if (messageText) {
connection.invoke("sendToConnection",connectionId,addConnectionToGroup: function(connectionId) {
confirm('Add connection ' + connectionId + ' to group: ' + this.defaultgroup);
connection.invoke("joinGroup",this.defaultgroup);
},addUserToGroup: function (user) {
r = confirm('Add user ' + user + ' to group: ' + this.defaultgroup);
connection.invoke("joinUserToGroup",removeConnectionIdFromGroup: function(connectionId) {
confirm('Remove connection ' + connectionId + ' from group: ' + this.defaultgroup);
connection.invoke("leaveGroup",removeUserFromGroup: function(user) {
confirm('Remove user ' + user + ' from group: ' + this.defaultgroup);
connection.invoke("leaveUserFromGroup",this.defaultgroup);
}
}
});
const apiBaseUrl = window.location.origin;
data.username = prompt("Enter your username");
const isAdmin = confirm('Work as administrator? (only an administrator can broadcast messages)');
if (!data.username) {
alert("No username entered. Reload page and try again.");
throw "No username entered";
}
const connection = new signalR.HubConnectionBuilder()
.withUrl(apiBaseUrl + '/api',{
accesstokenFactory: () => {
return generateAccesstoken(data.username)
}
})
.configureLogging(signalR.LogLevel.information)
.build();
connection.on('newMessage',onNewMessage);
connection.on('newConnection',onNewConnection)
connection.onclose(() => console.log('disconnected'));
console.log('connecting...');
connection.start()
.then(() => {
data.ready = true;
console.log('connected!');
})
.catch(console.error);
function getAxiosConfig() {
const config = {
headers: {
'x-ms-signalr-user-id': data.username,'Authorization': 'Bearer ' + generateAccesstoken(data.username)
}
};
return config;
}
let counter = 0;
function onNewMessage(message) {
message.id = counter++; // vue transitions need an id
data.messages.unshift(message);
};
function onNewConnection(message) {
data.myConnectionId = message.ConnectionId;
authEnabled = false;
if (message.Authentication)
{
authEnabled = true;
}
newConnectionMessage = {
id : counter++,text : `${message.ConnectionId} has connected,with Authorization: ${authEnabled.toString()}`
};
data.messages.unshift(newConnectionMessage);
}
function base64url(source) {
// Encode in classical base64
encodedSource = CryptoJS.enc.Base64.stringify(source);
// Remove padding equal characters
encodedSource = encodedSource.replace(/=+$/,'');
// Replace characters according to base64url specifications
encodedSource = encodedSource.replace(/\+/g,'-');
encodedSource = encodedSource.replace(/\//g,'_');
return encodedSource;
}
// this function should be in auth server,do not expose your secret
function generateAccesstoken(userName) {
var header = {
"alg": "HS256","typ": "JWT"
};
var stringifiedHeader = CryptoJS.enc.Utf8.parse(JSON.stringify(header));
var encodedHeader = base64url(stringifiedHeader);
// customize your JWT token payload here
var data = {
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": userName,"exp": 1699819025,'admin': isAdmin
};
var stringifiedData = CryptoJS.enc.Utf8.parse(JSON.stringify(data));
var encodedData = base64url(stringifiedData);
var token = encodedHeader + "." + encodedData;
var secret = "myfunctionauthtest"; // do not expose your secret here
var signature = CryptoJS.HmacSHA256(token,secret);
signature = base64url(signature);
var signedToken = token + "." + signature;
return signedToken;
}
</script>
</body>
</html>
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)