- 單對單私聊,網上查了很多資料,都沒效果,要麼不可取,如果不知道的JWT的,建議你先學會用JWT。有不對的地方望指出,互相學習
我看到有人把一個用戶當着一個組,用來單對單私聊,效果是可以的,但是我個人覺得這樣不對,我的方法是初始化時,把用戶id跟signalr的connectionid存放到一個字典中,私聊時,傳入私聊對象的用戶Id,然後根據字典獲取到該對象的connectionid,發送過去就可以了
1,首先引入signalr,nuget中搜索安裝就好,這裏不多介紹,Microsoft.AspNetCore.SignalR.Common,不壓縮的話就這個包就好
2,startup配置,ConfigureServices中
services.AddSignalR().AddMessagePackProtocol();
Configure中,爲了給前端signalr請求加上token,token的定義可能不一樣,你們自己定義
app.Use((context, next) =>
{
if (context.Request.Query.TryGetValue("Bearer", out var token))
context.Request.Headers.Add("Authorization", $"Bearer {token}");
return next.Invoke();
});
3,創建ChatHub類,繼承Hub,注意要加上 [Authorize]驗證特性
namespace AntAdmnin.Hubs
{
[Authorize]
public class ChatHub: Hub
{
//發送消息--發送給所有連接的客戶端
//public async Task SendMessage(string msg)
//{
// await Clients.All.SendAsync("ReceiveMessage", msg);
//}
////發送消息--發送給指定用戶
//public async Task SendPrivateMessage(string userId, string message)
//{
// await Clients.User(userId).SendAsync("ReceiveMessage", message);
//}
public override async Task OnConnectedAsync()
{
//await Clients.All.OnNotify(new { UserId= Context.User.Identity.Name, Name=Context.User.Identity.Name, ConnectId = Context.ConnectionId });
var userId = Context.User.Identity.Name;
//把上線的用戶跟signalr connectionid保存起來
SignalrHelper.UserAndSignalrDc[userId] =Context.ConnectionId;
// 思路 創建一個組時 ,把用戶的connectionid跟組名添加進signalr ,如果是持久性保存的組,可初始化時執行一遍組保存
// 例如這裏把所有的用戶添加到一個叫 company的組 ,要先初始化用戶關聯字典
foreach (var item in SignalrHelper.UserAndSignalrDc)
{
Groups.AddToGroupAsync(item.Value, "company");
}
await base.OnConnectedAsync();
}
}
}
4,創建一個字典,用來保存用戶跟signalr連接id的關聯,這應該是第三步纔對,在上面signalr連接的時候保存數據
public class SignalrHelper
{
/// <summary>
/// key是用戶Id ,value 是connectionid
/// </summary>
public static Dictionary<string, string> UserAndSignalrDc = new Dictionary<string, string>();
}
5,創建一個api控制器,ChatController,我這裏用了autofac注入,不用的自己實例化就好,也可以不用控制器,直接請求chathub也是可以的
[Authorize]
[Route("api/[controller]/[action]")]
[ApiController]
public class ChatController : ControllerBase
{
private IHubContext<ChatHub> chatHub;
public ChatController(IHubContext<ChatHub> chatHub)
{
this.chatHub = chatHub;
}
[HttpPost]
public async Task<IActionResult> SendAllMsg(ChatRecordInput input)
{
try
{
chatHub.Clients.Group(input.Receiver.ToString()).SendAsync("ReceiveMessage", input.Msg);
await chatHub.Clients.All.SendAsync("ReceiveMessage", input.Msg);
return Ok(Result.Success());
}
catch (Exception ex)
{
throw ex;
}
}
[HttpPost]
public async Task<IActionResult> SendGroupMsg(ChatRecordInput input)
{
try
{
await chatHub.Clients.Group("company").SendAsync("ReceiveMessage", input.Msg);
return Ok(Result.Success());
}
catch (Exception ex)
{
throw ex;
}
}
[HttpPost]
public async Task<IActionResult> SendUserMsg(ChatRecordInput input)
{
try
{
var connectionId = SignalrHelper.UserAndSignalrDc[input.Receiver.ToString()];
await chatHub.Clients.Clients(connectionId).SendAsync("ReceiveMessage", input.Msg);
return Ok(Result.Success());
}
catch (Exception ex)
{
throw ex;
}
}
後端就是這樣了,接下來是前端,用的vue
7, 同樣先安裝包 ,我這裏用的yarn,npm的安裝方式我試過好像安裝不上的,不知道爲什麼,安裝這個包花了很久,各種安裝不上,知道的大佬說一下怎麼回事
yarn add @aspnet/signalr
8,頁面,ui我用的是ant, 大家可以只參考js部分,前後端的接收方法名得保持一致,這個應該都知道吧
<template>
<div id="components-layout-demo-basic">
<a-layout>
<a-layout-sider>
<a-list itemLayout="horizontal" :dataSource="friends">
<a-list-item
@click="chooseFriend(item.userId)"
class="fItem"
slot="renderItem"
slot-scope="item"
>
<a-list-item-meta
description="Ant Design, a design language for background applications"
>
<p class="nick" slot="title">{{ item.nickName }}</p>
<a-avatar slot="avatar" :src="avatarFilter(item.avart)" />
</a-list-item-meta>
</a-list-item>
</a-list>
</a-layout-sider>
<a-layout>
<a-layout-content>
<div style="height:370px; width:98%; background: #fff; margin:5px; text-align: center;">
<p style="color:red">{{ remsg }}</p>
</div>
</a-layout-content>
<a-layout-footer>
<a-textarea v-model="msg" placeholder="enter message" :rows="4" />
<a-button @click="sendMsg" type="primary">發送信息</a-button>
</a-layout-footer>
</a-layout>
</a-layout>
</div>
</template>
<script>
import { getFriendList, sendMsgPrivate } from '@/api/chat'
import { getImgUrl } from '@/utils/util'
import * as signalR from '@microsoft/signalr'
const signalRMsgPack = require('@aspnet/signalr-protocol-msgpack')
let connection = null
export default {
data () {
return {
friends: [],
sendUserId: '',
msg: '',
remsg: ''
}
},
created () {
this.fetchData()
},
computed: {
avatarFilter () {
return function (avatar) {
return getImgUrl(avatar)
}
}
},
mounted () {
console.log('開始連接' + this.$store.getters.token)
connection = new signalR.HubConnectionBuilder()
.withAutomaticReconnect()
.withHubProtocol(new signalRMsgPack.MessagePackHubProtocol())
.withUrl(process.env.VUE_APP_API_BASE_URL + '/chatHub?Bearer=' + this.$store.getters.token, { accessTokenFactory: () => 'Bearer ' + this.$store.getters.token })
.build()
connection.start().catch(err => alert(err.message))
var _this = this
// 實現Show方法
connection.on('ReceiveMessage', function (username, message) {
_this.remsg = _this.remsg + '<br>' + username + ':' + message
})
},
methods: {
fetchData () {
getFriendList().then(response => {
console.log(response)
this.friends = response
})
},
sendMsg () {
if (this.msg.trim() === '') {
alert('不能發送空白消息')
return
}
var para = {
receiver: this.sendUserId,
msg: this.msg
}
console.log('私聊')
sendMsgPrivate(para).then(response => {})
// 調用後端方法 SendMsg 傳入參數
// connection.invoke("SendMsg", this.user, this.msg);
this.msg = ''
},
chooseFriend (userId) {
this.sendUserId = userId
}
}
}
</script>
<style>
#components-layout-demo-basic {
text-align: center;
}
#components-layout-demo-basic .ant-layout-header,
#components-layout-demo-basic .ant-layout-footer {
background: #7dbcea;
color: #fff;
height: 100px;
}
#components-layout-demo-basic .ant-layout-footer {
line-height: 1.5;
}
#components-layout-demo-basic .ant-layout-sider {
background: #3ba0e9;
color: #fff;
line-height: 500px;
}
#components-layout-demo-basic .ant-layout-content {
background: rgba(16, 142, 233, 1);
color: #fff;
min-height: 400px;
line-height: 120px;
}
#components-layout-demo-basic > .ant-layout {
margin-bottom: 48px;
}
#components-layout-demo-basic > .ant-layout:last-child {
margin: 0;
}
.nick {
color: red;
}
.fItem {
margin-top: 5px;
}
</style>