# SignalR 說明 & 簡易彈性呼叫畫面 ###### tags: `.NetCore` ## 後端 ### 套件 ``` Microsoft.AspNetCore.SignalR.Core ``` ### 建立 Hub Class ``` using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRChat.Hubs { [Authorize] public class ChatHub : Hub { public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } public async Task SendMessage_MoiraTest(string user, string message) { // 從 SignalR 的 HubCallerContext 取得登入的 User 帳號 var testGetUserName = Context.User.Identity.Name; var testGetUserName2 = Context.User.FindFirstValue(ClaimTypes.Name); // 從 DB 取得 User var testGetUser = _context.Users.Where(u => u.UserName == IdentityName).FirstOrDefault(); await Clients.All.SendAsync("ReceiveMessage", user, message); } } } ``` ### Startup.cs ``` public void ConfigureServices(IServiceCollection services) { services.AddSignalR(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseSignalR(routes => { routes.MapHub<ChatHub>("/chatHub"); }); app.UseDefaultFiles(); app.UseStaticFiles(); } ``` ## 前端 ### 套件 ``` microsoft-signalr ``` ### 直接看 簡易彈性呼叫畫面 ``` <div class="container"> <button onclick="bringInMoiraAccount()"> Bring In Moira Account </button> <div class="row">&nbsp;</div> <div class="row"> <div class="col-2">account</div> <div class="col-4"><input type="text" id="account" /></div> </div> <div class="row"> <div class="col-2">password</div> <div class="col-4"><input type="text" id="password" /></div> </div> <div class="row"> <div class="col-2">call api method</div> <div class="col-4"><input type="text" id="apiMethod" /></div> </div> <div class="row"> <div class="col-2">response method</div> <div class="col-4"><input type="text" id="responseMethod" /></div> </div> <div class="row"> <div class="col-2">requestBody</div> <div class="col-4"><textarea type="text" id="apiRequestBody" style="height:200px;width:300px"></textarea></div> </div> <div class="row">&nbsp;</div> <div class="row"> <div class="col-6"> <input type="button" id="loingAndConnect" onclick="LoingAndConnectAndSend()" value="Loing & Connect SignalR Server" /> </div> </div> <div class="row">&nbsp;</div> <div class="row"> <div class="col-6"> <input type="button" id="sendButton" value="Send Api" /> </div> </div> </div> <div class="row"> <div class="col-12"> <hr /> </div> </div> <div class="row"> <div class="col-6"> <ul id="messagesList"></ul> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.min.js"></script> <script> function bringInMoiraAccount() { document.getElementById('account').value = 'moira_han@------'; document.getElementById('password').value = '------' } function LoingAndConnectAndSend() { // Login var url = 'https://localhost:44343/api/Account/Login'; var data = { "email": document.getElementById('account').value, "password": document.getElementById('password').value }; fetch(url, { method: 'POST', // or 'PUT' body: JSON.stringify(data), // data can be `string` or {object}! headers: new Headers({ 'Content-Type': 'application/json' }) }).then(res => res.json()) .catch(error => console.error('Error:', error)) //.then(response => console.log('Success:', response)) // 登入成功才連線 .then(function (response) { console.log('Success:', response) var connection = new signalR.HubConnectionBuilder() .withUrl("/internalChat", { transport: signalR.HttpTransportType.LongPolling, accessTokenFactory: () => response.token }) .configureLogging(signalR.LogLevel.Debug) .build(); document.getElementById("sendButton").disabled = true; //connection.on("LoadInternalChatRoom", function (result) { // var li = document.createElement("li"); // li.textContent = "** Message From LoadInternalChatRoom: **" + JSON.stringify(result); // document.getElementById("messagesList").appendChild(li); //}); //connection.on("LoadMessageCenter", function (result) { // var li = document.createElement("li"); // li.textContent = "** Message From LoadMessageCenter: **" + JSON.stringify(result); // document.getElementById("messagesList").appendChild(li); //}); connection.on(document.getElementById("responseMethod").value, function (result) { var li = document.createElement("li"); li.textContent = "** Message From Targat: **" + JSON.stringify(result); document.getElementById("messagesList").appendChild(li); }); connection.start().then(function () { document.getElementById("sendButton").disabled = false; }).catch(function (err) { return console.log(err.toString()); }); document.getElementById("sendButton").addEventListener("click", function (event) { connection.invoke(document.getElementById("apiMethod").value, JSON.parse(document.getElementById("apiRequestBody").value)).catch(function (err) { return console.log(err.toString()); }); event.preventDefault(); }); }); } </script> ``` ### 簡易頁面畫面使用說明 ![](https://i.imgur.com/pa2sINt.png) [參考 - IThome 來玩玩即時互動App吧! ASP.NET Core SignalR系列](https://ithelp.ithome.com.tw/articles/10202038) ## 舊版 MVC4 .NETFramework 4.0 ### 套件使用 * Nuget Microsoft.AspNet.SignalR 1.2.2 * jQuery 1.7.1 * jquery.signalR-1.2.2.min.js ( Nuget 安裝時會一併安裝 ) ### 後端 #### Global.asax ``` public class MvcApplication : System.Web.HttpApplication, IDisposable { protected void Application_Start() { // 註冊 Hubs 應該要寫在第一行 RouteTable.Routes.MapHubs(); AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); WebApiConfig.Register(GlobalConfiguration.Configuration); LogUtility.CreateFileLogger(AppConfig.LogPath, true, LogFileRollingStyle.Composite, 100, 10, true); } } ``` #### Hub Class ``` public class ChatHub : Hub { public void Send(string name, string message) { // Call the addNewMessageToPage method to update clients. Clients.All.addNewMessageToPage(name, message); } } ``` #### Controller ``` public ActionResult Index() { return View(); } public ActionResult DoSomething() { AddConsoleMessage("讀取的,檔案中有個異常: XXXX"); return Json(new APIResult() { IsSuccess = true, Data = null, ErrorMessage = "沒有檔案可讀取", }); } private void AddConsoleMessage(string message) { // 後端廣播訊息出去 var hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>(); // Clients.All表示是發給這個頻道的所有人 // 如果要指定 群組 的話寫 => hubContext.Clients.Group(groupName, excludeConnectionIds).addNewMessageToConsole(message); // 如果要指定 某人 的話寫 => hubContext.Clients.Client(connectionId).addNewMessageToConsole(message); // 但目前還不確定用甚麼方式去取得群組和某人的資訊 hubContext.Clients.All.addNewMessageToPage(message); LogUtility.AppendLog(message, MXIC.MIT.Common.LogLevel.Info); } ``` #### 前端 ``` @{ ViewBag.Title = "Room"; } <div class="container"> <input type="text" id="message" /> <input type="button" id="sendmessage" value="Send" /> <input type="hidden" id="displayname" /> <ul id="discussion"> </ul> </div> @* 引用 jQuery 和官方前端套件 *@ <script src="~/Scripts/jquery-1.7.1.min.js"></script> <script src="~/Scripts/jquery.signalR-1.2.2.min.js"></script> @* 自動產生 (auto-generated),引用這個才能抓到後端宣告的 Hub。 $.connection.chatHub *@ <script src="~/signalr/hubs"></script> <script> $(function () { // Reference the auto-generated proxy for the hub. var chat = $.connection.chatHub; // Create a function that the hub can call back to display messages. chat.client.addNewMessageToPage = function (name, message) { // Add the message to the page. $('#discussion').append('<li><strong>' + htmlEncode(name) + '</strong>: ' + htmlEncode(message) + '</li>'); }; // Get the user name and store it to prepend to messages. $('#displayname').val(prompt('Enter your name:', '')); // Set initial focus to message input box. $('#message').focus(); // Start the connection. $.connection.hub.start().done(function () { $('#sendmessage').click(function () { // Call the Send method on the hub. chat.server.send($('#displayname').val(), $('#message').val()); // Clear text box and reset focus for next comment. $('#message').val('').focus(); }); }); }) // This optional function html-encodes messages for display in the page. function htmlEncode(value) { var encodedValue = $('<div />').text(value).html(); return encodedValue; } </script> ``` ### 如何持續重新連線 **SignalR 預設的中斷連線逾時為 30 秒** 在某些應用程式中,您可能會想要在連線遺失後自動重新建立連線,而且嘗試重新連線已逾時。若要這樣做,您可以從事件處理常式呼叫 Start 方法 Closed , disconnected (JavaScript 用戶端上的事件處理常式) 。 您可能會想要等候一段時間,再呼叫 Start ,以避免在伺服器或實體連線無法使用時太頻繁地執行此動作。 下列程式碼範例適用于使用產生的 Proxy 的 JavaScript 用戶端。 ``` $.connection.hub.disconnected(function() { setTimeout(function() { $.connection.hub.start(); }, 5000); // Restart connection after 5 seconds. }); ``` [了解及處理 SignalR 的連線存留期事件](https://learn.microsoft.com/zh-tw/aspnet/signalr/overview/guide-to-the-api/handling-connection-lifetime-events)