Vue + SignalR + .NET Core 2.2 通讯
1. 引用SignalR
服务端略
Vue 添加 依赖 @microsoft/signalr
npm install @microsoft/signalr
2. 服务端注册使用
ConfigureServices 中加入 AddHttpContextAccessor 和 AddSignalR
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
//获取连接ID用的
services.AddHttpContextAccessor();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
//添加SignalR
services.AddSignalR();
//MQ的注入
services.AddSingleton<IMyMQ, MyMQ>();
}
Configure中
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
/*这里引用的顺序如果不对会报跨域错误*/
app.UseCors(builder => {
//builder.WithOrigins(""http://127.0.0.1:44325");//指定IP
//builder.AllowAnyOrigin();//这个不好使
builder.SetIsOriginAllowed(origin => true);
builder.AllowAnyHeader();
builder.AllowAnyMethod();
builder.AllowCredentials();
});
app.UseWebSockets();
//取个名Home 这个随便 前端跟他一样就行
//ViewHub 是新建的类
app.UseSignalR(routes =>
{
routes.MapHub<ViewHub>("/Home");
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
//app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
ViewHub 类继承 Hub
public class ViewHub : Hub
{
/// <summary>
/// websocket SignalR
/// </summary>
private readonly IHubContext<ViewHub> _messageHub;
/// <summary>
/// MQ
/// </summary>
private readonly IMyMQ _mymq;
private readonly IHttpContextAccessor _accessor;
//第一个是我的MQ注入,第二个是连接的注入
public ViewHub(IMyMQ mymq, IHubContext<ViewHub> messageHub,IHttpContextAccessor accessor)
{
this._mymq = mymq;
this._messageHub = messageHub;
this._accessor = accessor;
//发送消息
messageHub.Clients.All.SendAsync("MQLOG", "XXXX");
}
public override Task OnConnectedAsync()
{
//可以拿到当前连接的ID 作为标识
string connId = Context.ConnectionId;
//在连接时还可以传递其他信息
//var token = _accessor.HttpContext.Request.Query["access_token"];
//给当前连接返回消息 .Clients可以发多个连接ID
Clients.Client(connId).SendAsync("ConnectResponse", id+"连接成功");
return base.OnConnectedAsync();
}
//接收前端来的消息
public async Task SendMessage(string theme, string data,string GUID)
{
JObject msg = new JObject();
msg.Add("msg","发送成功");
msg.Add("GUID", GUID);
//也可以这样直接发送消息
await Clients.All.SendAsync("MQSend", msg.ToString());
}
}
3. Vue端连接使用
建一个signalR.js
import * as signalR from "@microsoft/signalr";
//Url 注意/Home 跟服务端一致
const url = "http://localhost:63437/Home";
const signal = new signalR.HubConnectionBuilder() //服务器地址
.withUrl(url, {
//传递Token
accessTokenFactory: () => "00001"
})
.withAutomaticReconnect()//自动重连
.build();
async function start() {
try {
await signal.start();
console.log("connected");
} catch (err) {
console.log(err);
//半自动重连
//setTimeout(() => start(), 5000);
}
}
//关闭
signal.onclose(async err => {
//await start();
console.log(err);
});
//将创建的signal赋值给Vue实例
export default {
//install方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象。
install: function(Vue) {
Vue.prototype.signalr = signal;
}
};
在main.js中引用
import signalr from "./utils/signalR";
Vue.use(signalr);
连接,这里写在了App.vue中
created() {
//接收消息建议在start之前注册,至少注册一个
//参照服务端 连接之后会有个ConnectResponse返回ID
this.signalr.on("ConnectResponse", res => {
//可以做相关业务逻辑
console.log(res);
});
this.signalr.on("MQSend", res => {
res = JSON.parse(res);
});
},
mounted() {
this.signalr.start().then(() => {
console.log("连接");
});
},
methods: {
//向服务端发送消息
async setMessage(theme, data) {
return new Promise((resolve, reject) => {
//对应服务端的SendMessage方法
this.signalr
.invoke("SendMessage", theme, JSON.stringify(data), guid)
.catch(function(err) {
console.error(err.toString())
//reject(err.toString());
});
resolve('done')
});
});
}
4. 其他-invoke的响应
到这就可以了,但是使用中 invoke 是不返回消息的,如果返回消息还要再下发,在 on 里接收,这是两个独立事件,业务页面我要先写个监听返回再写个发送方法,如果我能写成 await 形式就方便了,比如
let res = await this.App.setMessage("XXX", {XXXX});
this.$message(res.msg);
这里要将App.vue中的setMessage重写,有返回结果后再执行Promise的resolve,
async setMessage(theme, data) {
//先取一个标识
const guid = this.$newGUID();
//存储当前的Promise
let prom = {};
//创建Promise 将GUID一并上传
let p = new Promise((resolve, reject) => {
this.signalr
.invoke("SendMessage", theme, JSON.stringify(data), guid)
//将resolve 缓存
prom.resolve = resolve;
});
//将Promise缓存
prom.p = p;
//放到页面缓存
this.MQsend[guid] = prom;
//由于没有执行resolve 这个请求将一直挂起 直到执行resolve
return p;
}
App.vue接收结果改为
this.signalr.on("MQSend", res => {
res = JSON.parse(res);
/**
*服务返回GUID
*先找到缓存中GUID对应的Promise
*再执行resolve 执行后删除缓存
*这时Promise的状态改变 await结束
*/
if (res.GUID && this.MQsend[res.GUID]) {
this.MQsend[res.GUID].resolve(res);
delete this.MQsend[res.GUID];
}
});