使用SignalR ASP.NET Core來簡單實現一個後臺實時推送數據給Echarts展現圖表的功能

什麼是 SignalR ASP.NET Core
javascript

ASP.NET Core SignalR 是一種開放源代碼庫,可簡化將實時 web 功能添加到應用程序的功能。 實時 web 功能使服務器端代碼能夠當即將內容推送到客戶端。html

SignalR ASP.NET Core能夠作什麼前端

• 須要從服務器進行高頻率更新的應用。 示例包括遊戲、社交網絡、投票、拍賣、地圖和 GPS 應用。
• 儀表板和監視應用。 示例包括公司儀表板、即時銷售更新或旅行警報。
• 協做應用。 協做應用的示例包括白板應用和團隊會議軟件。
• 須要通知的應用。 社交網絡、電子郵件、聊天、遊戲、旅行警報和不少其餘應用都需使用通知。java

SignalR  ASP.NET Core特點jquery

• 自動處理鏈接管理。
• 可將消息同時發送到全部鏈接的客戶端。
• 可向特定客戶端或客戶端組發送消息。
• 可縮放以處理不斷增長的流量。
• SignalR採用rpc來進行客戶端與服務器端之間的通訊。
• SignalR會自動選擇服務器和客戶端的最佳傳輸方法(WebSockets、Server-Sent事件、長輪詢)SignalR能夠根據當前瀏覽器所支持的協議來選擇最優的鏈接方式,從而可讓咱們把更多的精力放在業務上而不是底層傳輸技術上。web

哪些瀏覽器支持SignalR  ASP.NET Coreajax

Apple Safari(包含IOS端)、Google Chrome(包括 Android端)、Microsoft Edge、Mozilla Firefox等主流瀏覽器都支持SignalR  ASP.NET Core。json

本次咱們將實現一個經過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來展現圖標的效果。

相關文章
相關標籤/搜索