# 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"> </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"> </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"> </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>
```
### 簡易頁面畫面使用說明

[參考 - 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)