使用SignalR ASP.NET Core来简单实现一个后台实时推送数据给Echarts展示图表的功能
source link: https://www.cnblogs.com/wdw984/p/14645614.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
使用SignalR ASP.NET Core来简单实现一个后台实时推送数据给Echarts展示图表的功能
什么是 SignalR ASP.NET Core
ASP.NET Core SignalR 是一种开放源代码库,可简化将实时 web 功能添加到应用程序的功能。 实时 web 功能使服务器端代码可以立即将内容推送到客户端。
SignalR ASP.NET Core可以做什么
• 需要从服务器进行高频率更新的应用。 示例包括游戏、社交网络、投票、拍卖、地图和 GPS 应用。
• 仪表板和监视应用。 示例包括公司仪表板、即时销售更新或旅行警报。
• 协作应用。 协作应用的示例包括白板应用和团队会议软件。
• 需要通知的应用。 社交网络、电子邮件、聊天、游戏、旅行警报和很多其他应用都需使用通知。
SignalR ASP.NET Core特色
• 自动处理连接管理。
• 可将消息同时发送到所有连接的客户端。
• 可向特定客户端或客户端组发送消息。
• 可缩放以处理不断增加的流量。
• SignalR采用rpc来进行客户端与服务器端之间的通信。
• SignalR会自动选择服务器和客户端的最佳传输方法(WebSockets、Server-Sent事件、长轮询)SignalR可以根据当前浏览器所支持的协议来选择最优的连接方式,从而可以让我们把更多的精力放在业务上而不是底层传输技术上。
哪些浏览器支持SignalR ASP.NET Core
Apple Safari(包含IOS端)、Google Chrome(包括 Android端)、Microsoft Edge、Mozilla Firefox等主流浏览器都支持SignalR ASP.NET Core。
本次我们将实现一个通过SignalR来简单实现一个后台实时推送数据给Echarts来展示图表的功能
首先我们新建一个ASP.NET Core 3.1的web应用
随后我们引用SignalR ASP.NET Core、Jquery和Echarts的客户端库
在项目中我们新建以下目录
Class、HubInterface、Hubs
接着我们在Pages目录下新建如下目录
echarts
在Shared目录中新建一个Razor布局页(_LayoutEcharts.cshtml)
在echarts目录中新建一个Razor页面(Index.cshtml)
在Class目录中新建一个类(ClientMessageModel.cs)
在HubInterface目录中新建一个接口(IChatClient.cs)
在Hub目录中新建一个类(ChatHub.cs)
我们先实现后台逻辑代码,随后在编写前端交互代码。
在IChatClient.cs中,我们主要是定义统一的服务端调用客户端方法的统一方法名(防止每次都要手动输入调用方法是出现失误而导致调用失败的低级错误)
namespace
signalr.HubInterface
{
public
interface
IChatClient
{
/// <summary>
/// 客户端接收数据触发函数名
/// </summary>
/// <param name="clientMessageModel">消息实体类</param>
/// <returns></returns>
Task ReceiveMessage(ClientMessageModel clientMessageModel);
/// <summary>
/// Echart接收数据触发函数名
/// </summary>
/// <param name="data">JSON格式的可以被Echarts识别的data数据</param>
/// <returns></returns>
Task EchartsMessage(Array data);
/// <summary>
/// 客户端获取自己登录后的UID
/// </summary>
/// <param name="clientMessageModel">消息实体类</param>
/// <returns></returns>
Task GetMyId(ClientMessageModel clientMessageModel);
}
}
ClientMessageModel.cs中,我们主要定义的是序列化后的交互用的实体类
namespace
signalr.Class
{
/// <summary>
/// 服务端发送给客户端的信息
/// </summary>
[Serializable]
public
class
ClientMessageModel
{
/// <summary>
/// 接收用户编号
/// </summary>
public
string
UserId {
get
;
set
; }
/// <summary>
/// 组编号
/// </summary>
public
string
GroupName {
get
;
set
; }
/// <summary>
/// 发送的内容
/// </summary>
public
string
Context {
get
;
set
; }
}
}
在ChatHub.cs中,主要是实现SignalR集线器的核心功能,用来处理客户端<==>服务器交互代码。在这里我们继承了Hub<T>的方法,集成了我们定义的IChatClient接口,从而就可以在方法中直接调用接口名称来和客户端交互。
namespace
signalr.Hubs
{
public
class
ChatHub : Hub<IChatClient>
{
public
override
async Task OnConnectedAsync()
{
var
user = Context.ConnectionId;
await Clients.Client(user).GetMyId(
new
ClientMessageModel { UserId = user, Context = $
"回来了{DateTime.Now:yyyy-MM:dd HH:mm:ss}"
});
await Clients.AllExcept(user).ReceiveMessage(
new
ClientMessageModel { UserId = user, Context = $
"进来了{DateTime.Now:yyyy-MM:dd HH:mm:ss}"
});
await
base
.OnConnectedAsync();
}
public
override
async Task OnDisconnectedAsync(Exception exception)
{
var
user = Context.ConnectionId;
await Clients.All.ReceiveMessage(
new
ClientMessageModel { UserId = user, Context = $
"{user}离开了{DateTime.Now:yyyy-MM:dd HH:mm:ss}"
});
await
base
.OnDisconnectedAsync(exception);
}
}
}
我们重写了Hub的OnConnectedAsync方法,当有客户端连接进来的时候,我们给当前客户端发送一条“回来了”的内容,同时给所有在线的客户端发送一条“进来了”的通知,内容中会带上本次连接所分配给动态Guid编号。(类似与通知大家谁谁上线了)
在OnDisconnectedAsync方法中,当客户端断开连接的时候,会给所有在线客户端发送一条带有离线客户端的Guid的离开消息。(类似通知大家谁谁谁离开了)
在Startup.cs中,我们做以下设置(注入SignalR和注册Hub),同时先把在DEBUG模式下的XSRF禁用,否则访问接口会提示400错误
public
void
ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddRazorPages()
#if DEBUG
//Debug下禁用XSRF防护,方便调试
.AddRazorPagesOptions(o =>
{
o.Conventions.ConfigureFilter(
new
IgnoreAntiforgeryTokenAttribute());
})
#endif
;
}
public
void
Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if
(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(
"/Error"
);
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>(
"/chathub"
);
//注册hub
});
}
以上服务端的基架功能就搭建好了,下面我们会来实现后台推送数据给前台Echart的功能。
在_LayoutEcharts.cshtml布局页中,我们实现引用Jquery和Echarts的JS文件,同时编写一个请求后台接口的方法,调用这个方法后,后台就会主动推送多次数据给前台。
<!
DOCTYPE
html>
<
html
>
<
head
>
<
meta
charset="utf-8">
<
meta
name="viewport" content="width=device-width" />
<
script
src="~/lib/echarts/dist/echarts.min.js"></
script
>
<
script
src="~/lib/jquery/dist/jquery.js"></
script
>
<
title
>@ViewBag.Title</
title
>
<
script
>
function Test() {
var chartDom = document.getElementById('main');
var myChart = window.echarts.init(chartDom);
$.ajax({
url:'/echarts',
type:'POST',
dateType: 'json',
data: { user: user},
beforeSend: function (XHR) {
console.log('I am ' + user);
myChart.showLoading({
text: '加载中。。。',
effect: 'whirling'
});
},
success:function(data) {
var option = {
series: [{
data: data.data
}]
};
myChart.setOption(option);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown);
},
complete:function(XHR, TS) {
myChart.hideLoading();
}
});
}
</
script
>
</
head
>
<
body
>
<
div
>
@RenderBody()
</
div
>
@await RenderSectionAsync("Scripts", required: false)
</
body
>
</
html
>
在echarts目录的Index.cshtml中,我们实现引用Echarts组件,来渲染图表,引用SignalR来实现和服务器端数据实时交互。
@page
@model signalr.Pages.echarts.IndexModel
@{
ViewBag.Title = "Echarts图标展示(https://www.cnblogs.com/wdw984)";
Layout = "_LayoutEcharts";
}
<
div
id="main" style="width: 800px;height:600px;"></
div
>
<
button
onclick="Test()">测试</
button
>
<
script
type="text/javascript">
var app = {};
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;
var posList = [
'left', 'right', 'top', 'bottom',
'inside',
'insideTop', 'insideLeft', 'insideRight', 'insideBottom',
'insideTopLeft', 'insideTopRight', 'insideBottomLeft', 'insideBottomRight'
];
app.configParameters = {
rotate: {
min: -90,
max: 90
},
align: {
options: {
left: 'left',
center: 'center',
right: 'right'
}
},
verticalAlign: {
options: {
top: 'top',
middle: 'middle',
bottom: 'bottom'
}
},
position: {
options: posList.reduce(function (map, pos) {
map[pos] = pos;
return map;
}, {})
},
distance: {
min: 0,
max: 100
}
};
app.config = {
rotate: -25,
align: 'left',
verticalAlign: 'middle',
position: 'bottom',
distance: 15,
onChange: function () {
var labelOption = {
normal: {
rotate: app.config.rotate,
align: app.config.align,
verticalAlign: app.config.verticalAlign,
position: app.config.position,
distance: app.config.distance
}
};
myChart.setOption({
series: [{
label: labelOption
}, {
label: labelOption
}, {
label: labelOption
}, {
label: labelOption
}]
});
}
};
var labelOption = {
show: true,
position: app.config.position,
distance: app.config.distance,
align: app.config.align,
verticalAlign: app.config.verticalAlign,
rotate: app.config.rotate,
formatter: '{c} {name|{a}}',
fontSize: 16,
rich: {
name: {
}
}
};
option = {
title: {
text: '验证情况统计'
},
tooltip: {},
legend: {
},
xAxis: {
data: ['数据一','数据二', '数据三','',
'数据四', '数据五','',
'数据六', '数据七', '数据八','数据九','',
'数据十','数据十一','数据十二','数据十三','数据十四'],
axisTick: {show: false},
axisLabel:{rotate: -25,interval: 0}
},
yAxis: {},
series: [{
type: 'bar',
label: {
show: true,
position: 'outside'
},
itemStyle: {
normal: {
color: function(params) {
var colorList = [
"Blue",
"Blue",
"Blue",
"",
"LightSkyBlue",
"LightSkyBlue",
"",
"Gold",
"Gold",
"Gold",
"Gold",
"",
"LightGrey",
"LightGrey",
"LightGrey",
"LightGrey",
"LightGrey"
];
return colorList[params.dataIndex];
}
}
},
data: ['0','0','0','', '0', '0', '', '0','0','0','0','', '0','0','0','0','0']
}]
};
option && myChart.setOption(option);
</
script
>
@section Scripts
{
<
script
src="~/js/signalr/dist/browser/signalr.js"></
script
>
<
script
src="~/js/echartchat.js"></
script
>
}
在Index后台代码中,我们响应一个POST请求,请求中带上SignalR分配的唯一编号,后台模拟数据统计,推送给前台,这里用Task.Factory来创建一个任务执行这个操作。
public
async Task<JsonResult> OnPostAsync(
string
user)
{
if
(
string
.IsNullOrWhiteSpace(user))
{
return
new
JsonResult(
new
{ status =
"fail"
, message =
"NoUser"
});
}
await Task.Factory.StartNew(async () =>
{
var
rnd =
new
Random(DateTime.Now.Millisecond);
for
(
var
i = 0; i < 10; i++)
{
await _hubContext.Clients.Client(user)
.EchartsMessage(
new
[] {
$
"{rnd.Next(100,300)}"
,
$
"{rnd.Next(100,320)}"
,
$
"{rnd.Next(100,310)}"
,
""
,
$
"{rnd.Next(10,30)}"
,
$
"{rnd.Next(10,30)}"
,
""
,
$
"{rnd.Next(130,310)}"
,
$
"{rnd.Next(130,310)}"
,
$
"{rnd.Next(13,31)}"
,
$
"{rnd.Next(13,31)}"
,
""
,
$
"{rnd.Next(130,310)}"
,
$
"{rnd.Next(130,310)}"
,
$
"{rnd.Next(13,31)}"
,
$
"{rnd.Next(130,310)}"
,
$
"{rnd.Next(130,310)}"
}
);
await Task.Delay(2000);
}
}, TaskCreationOptions.LongRunning);
return
new
JsonResult(
new
{ status =
"ok"
});
}
随后我们访问以下这个页面,就可以看到目前这种效果
下面我们来编写前端js,用来和后端服务通过SignalR通信,在wwwroot/js下新建一个echartchat.js
"use strict"
;
var
connection =
new
signalR.HubConnectionBuilder()
.withUrl(
"/chatHub"
)
.withAutomaticReconnect()
.configureLogging(signalR.LogLevel.Debug)
.build();
var
user =
""
;
var
chartDom = document.getElementById(
'main'
);
var
myChart = window.echarts.init(chartDom);
connection.on(
"GetMyId"
,
function
(data) {
user = data.userId;
//SignalR返回的数据字段开头是小写
console.log(user);
});
connection.on(
"ReceiveMessage"
,
function
(data) {
console.log(data.userId + data.context);
});
connection.on(
"EchartsMessage"
,
function
(data) {
console.log(data);
var
option = {
series: [{
data: data
}]
};
myChart.setOption(option);
//更新Echarts数据
});
connection.start().then(
function
() {
console.log(
"服务器已连接"
);
}).
catch
(
function
(err) {
return
console.error(err.toString());
});
保存后我们再次访问页面,并点击按钮,就可以实现后台推送数据给前台echarts来展示图标的效果。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK