爲了讓客戶端可以鏈接到 Hub ,當程序啓動的時候你須要調用 MapSignalR 方法。html
下面代碼顯示瞭如何在 OWIN startup 類裏面定義 SignalR Hubs 路由。web
using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(MyApplication.Startup))] namespace MyApplication { public class Startup { public void Configuration(IAppBuilder app) { // Any connection or hub wire up and configuration should go here app.MapSignalR(); } } }
默認狀況下,客戶端都是經過 "/signalr" 路由地址來鏈接到你的 Hub,你也能夠修改不使用默認的 "/signalr"。api
服務端代碼指定Url數組
app.MapSignalR("/signalrTest", new HubConfiguration());
JavaScript 客戶端代碼指定Urlcookie
<script src="signalrTest/hubs"></script> var chat = $.connection.chatHub; chat.url = "/signalrTest";
.NET 客戶端代碼指定Urlapp
var Connection = new HubConnection("http://localhost:8080/signalrTest");
爲了建立 Hub 類,你須要建立一個繼承於 Microsoft.Aspnet.Signalr.Hub 的類異步
public class ContosoChatHub : Hub { public void NewContosoChatMessage(string name, string message) { Clients.All.addNewMessageToPage(name, message); } }
Server (駝峯命名法,第一個字母能夠爲大寫也能夠爲小寫)async
public class ContosoChatHub : Hub
JavaScript (無論 Server 端第一個字母爲大小仍是小寫,JavaScript 客戶端必須是小寫,否則就找不到對應的 Hub)ide
var contosoChatHubProxy = $.connection.contosoChatHub;
當使用 特性 HubName命名 Hub 的名字,那麼 Server 端和 Client 端的 Hub 名大小寫必須保持一致。ui
Server (若是 HubName 指定的第一個字母爲大寫,那麼 JavaScript 端也必須爲大寫。若是是小寫,那麼 JavaScript 端也必須爲小寫)
[HubName("PascalCaseContosoChatHub")] public class ContosoChatHub : Hub
JavaScript
var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;
定義一個接口 T,讓你的 Hub 類繼承於 Hub<T>,這樣就能夠指定你的客戶端能夠調用的方法,也能夠在你的 Hub 方法裏開啓代碼提示。
public class StrongHub : Hub<IClient> { public void Send(string message) { Clients.All.NewMessage(message); } } public interface IClient { void NewMessage(string message); }
若是須要暴露一個能夠在客戶端調用的方法,那麼須要在 Hub 裏定義一個 public 的方法,以下所示。
public class ContosoChatHub : Hub { public void NewContosoChatMessage(string name, string message) { Clients.All.addNewMessageToPage(name, message); } }
public class StockTickerHub : Hub { public IEnumerable<Stock> GetAllStocks() { return _stockTicker.GetAllStocks(); } }
你能夠指定方法的參數和返回類型,包含複雜類型和數組。這些數據在客戶端和服務端會使用 Json 來進行傳輸,SignalR 會自動綁定複雜類型對象和數組對象。
Server (駝峯命名法,第一個字母能夠爲大寫也能夠爲小寫)
public void NewContosoChatMessage(string userName, string message)
JavaScript (無論 Server 端第一個字母爲大小仍是小寫,JavaScript 客戶端必須是小寫,否則就找不到對應的 方法)
contosoChatHubProxy.server.newContosoChatMessage(userName, message);
使用特性 HubMethodName 指定方法名,那麼 Server 端和 Client 端的 方法名大小寫必須保持一致。
Server 端
[HubMethodName("PascalCaseNewContosoChatMessage")] public void NewContosoChatMessage(string userName, string message)
JavaScript 端
contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);
你能夠在 Hub 類方法中使用 Clients 屬性來調用客戶端的方法
Server 端
public class ContosoChatHub : Hub { public void NewContosoChatMessage(string name, string message) { Clients.All.addNewMessageToPage(name, message); } }
JavaScript 端
contosoChatHubProxy.client.addNewMessageToPage = function (name, message) { // Add the message to the page. $('#discussion').append('<li><strong>' + htmlEncode(name) + '</strong>: ' + htmlEncode(message) + '<li>'); };
你不能從客戶端獲得一個返回值,好比 int x = Clients.All.add(1,1) 是沒有用的。
你能夠給參數指定複雜類型和數組類型,以下所示。
Sever 端
public void SendMessage(string name, string message) { Clients.All.addContosoChatMessageToPage(new ContosoChatMessage() { UserName = name, Message = message }); } public class ContosoChatMessage { public string UserName { get; set; } public string Message { get; set; } }
JavaScript 代碼
var contosoChatHubProxy = $.connection.contosoChatHub; contosoChatHubProxy.client.addMessageToPage = function (message) { console.log(message.UserName + ' ' + message.Message); });
全部鏈接的客戶端
Clients.All.addContosoChatMessageToPage(name, message);
只是 Calling 的客戶端
Clients.Caller.addContosoChatMessageToPage(name, message);
全部鏈接的客戶端除了 Calling 的客戶端
Clients.Others.addContosoChatMessageToPage(name, message);
經過 connection ID 指定特定的客戶端
Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);
經過 connection ID 排除特定的客戶端
Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
指定一個特殊組
Clients.Group(groupName).addContosoChatMessageToPage(name, message);
指定一個特殊組,而且排除特定 connection ID 的客戶端
Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
指定一個特殊組,可是排除 calling
Clients.OthersInGroup(groupName).addContosoChatMessageToPage(name, message);
經過 userId 指定一個特殊的用戶,通常狀況下是 IPrincipal.Identity.Name
Clients.User(userid).addContosoChatMessageToPage(name, message);
在一個 connection IDs 列表裏的全部客戶端和組
Clients.Clients(ConnectionIds).broadcastMessage(name, message);
指定多個組
Clients.Groups(GroupIds).broadcastMessage(name, message);
經過用戶名
Clients.Client(username).broadcastMessage(name, message);
一組用戶名
Clients.Users(new string[] { "myUser", "myUser2" }).broadcastMessage(name, message);
客戶端方法名的調用是大小寫不敏感的,好比 Clients.All.addContosoChatMessageToPage 會調用客戶端的 AddContosoChatMessageToPage, addcontosochatmessagetopage, or addContosoChatMessageToPage 這些方法。
public async Task NewContosoChatMessage(string name, string message) { await Clients.Others.addContosoChatMessageToPage(data); Clients.Caller.notifyMessageSent(); }
若是你須要使用一個 string 做爲方法名,那麼你須要把 Clients.All (or Clients.Others, Clients.Caller, etc.) 對象賦值給 IClientProxy,而後調用它的 Invoke(methodName, args...) 方法。
public void NewContosoChatMessage(string name, string message) { string methodToCall = "addContosoChatMessageToPage"; IClientProxy proxy = Clients.All; proxy.Invoke(methodToCall, name, message); }
Server 端
public class ContosoChatHub : Hub { public Task JoinGroup(string groupName) { return Groups.Add(Context.ConnectionId, groupName); } public Task LeaveGroup(string groupName) { return Groups.Remove(Context.ConnectionId, groupName); } }
客戶端
contosoChatHubProxy.server.joinGroup(groupName);
contosoChatHubProxy.server.leaveGroup(groupName);
異步執行
public async Task JoinGroup(string groupName) { await Groups.Add(Context.ConnectionId, groupName); Clients.Group(groupname).addContosoChatMessageToPage(Context.ConnectionId + " added to group"); }
public class ContosoChatHub : Hub { public override Task OnConnected() { // Add your own code here. // For example: in a chat application, record the association between // the current connection ID and user name, and mark the user as online. // After the code in this method completes, the client is informed that // the connection is established; for example, in a JavaScript client, // the start().done callback is executed. return base.OnConnected(); } public override Task OnDisconnected() { // Add your own code here. // For example: in a chat application, mark the user as offline, // delete the association between the current connection id and user name. return base.OnDisconnected(); } public override Task OnReconnected() { // Add your own code here. // For example: in a chat application, you might have marked the // user as offline after a period of inactivity; in that case // mark the user as online again. return base.OnReconnected(); } }
Calling 客戶端的 connection ID
string connectionID = Context.ConnectionId;
connection ID 是一個由SignalR分配的 GUID ( 你不能用本身的代碼指定 ). 每一個鏈接都有一個 connection ID , 若是你的應用裏包含多個 Hubs,那麼多個 Hubs也會共用同一個 connection ID .
Http Header 數據
System.Collections.Specialized.NameValueCollection headers = Context.Request.Headers;
System.Collections.Specialized.NameValueCollection headers = Context.Headers;
Query string 數據
System.Collections.Specialized.NameValueCollection queryString = Context.Request.QueryString; System.Collections.Specialized.NameValueCollection queryString = Context.QueryString; string parameterValue = queryString["parametername"];
JavaScript 客戶端 QueryString
$.connection.hub.qs = { "version" : "1.0" };
這邊的 $.connection.hub.qs 不是指你當前的 Hub ($.connection.chatHub.qs)。
Cookies
System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.Request.Cookies;
System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.RequestCookies;
用戶信息
System.Security.Principal.IPrincipal user = Context.User;
Request 的 HttpContext 對象
System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();
JavaScript 客戶端
contosoChatHubProxy.state.userName = "Fadi Fakhouri"; contosoChatHubProxy.state.computerName = "fadivm1";
Server 端獲取,可使用 Caller 或者 CallerState 兩種方式
string userName = Clients.Caller.userName; string computerName = Clients.Caller.computerName;
string userName = Clients.CallerState.userName; string computerName = Clients.CallerState.computerName;
如何在 Hub 類中捕獲異常
public class ErrorHandlingPipelineModule : HubPipelineModule { protected override void OnIncomingError(ExceptionContext exceptionContext, IHubIncomingInvokerContext invokerContext) { Debug.WriteLine("=> Exception " + exceptionContext.Error.Message); if (exceptionContext.Error.InnerException != null) { Debug.WriteLine("=> Inner Exception " + exceptionContext.Error.InnerException.Message); } base.OnIncomingError(exceptionContext, invokerContext); } } public void Configuration(IAppBuilder app) { // Any connection or hub wire up and configuration should go here GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule()); app.MapSignalR(); }
public class MyHub : Hub { public void Send(string message) { if(message.Contains("<script>")) { throw new HubException("This message will flow to the client", new { user = Context.User.Identity.Name, message = message }); } Clients.All.send(message); } }
若是須要啓動 Server 端日誌,那麼須要在 web.config 裏面添加一個 system.diagnostics 節點
<system.diagnostics> <sources> <source name="SignalR.SqlMessageBus"> <listeners> <add name="SignalR-Bus" /> </listeners> </source> <source name="SignalR.ServiceBusMessageBus"> <listeners> <add name="SignalR-Bus" /> </listeners> </source> <source name="SignalR.ScaleoutMessageBus"> <listeners> <add name="SignalR-Bus" /> </listeners> </source> <source name="SignalR.Transports.WebSocketTransport"> <listeners> <add name="SignalR-Transports" /> </listeners> </source> <source name="SignalR.Transports.ServerSentEventsTransport"> <listeners> <add name="SignalR-Transports" /> </listeners> </source> <source name="SignalR.Transports.ForeverFrameTransport"> <listeners> <add name="SignalR-Transports" /> </listeners> </source> <source name="SignalR.Transports.LongPollingTransport"> <listeners> <add name="SignalR-Transports" /> </listeners> </source> <source name="SignalR.Transports.TransportHeartBeat"> <listeners> <add name="SignalR-Transports" /> </listeners> </source> </sources> <switches> <add name="SignalRSwitch" value="Verbose" /> </switches> <sharedListeners> <add name="SignalR-Transports" type="System.Diagnostics.TextWriterTraceListener" initializeData="transports.log.txt" /> <add name="SignalR-Bus" type="System.Diagnostics.TextWriterTraceListener" initializeData="bus.log.txt" /> </sharedListeners> <trace autoflush="true" /> </system.diagnostics>
若是須要在 Hub 類外面調用客戶端方法和管理組,那麼須要獲取一個 SignalR context 對象,而後能夠經過 context.Clients 對象來操做客戶端方法和組。
IHubContext _context = GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>();
_context.Clients.All.updateStockPrice(stock);
SignalR 容許你把本身的代碼注入 Hub 管道模型,你能夠經過繼承 HubPipelineModule 來實現
public class LoggingPipelineModule : HubPipelineModule { protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context) { Debug.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name); return base.OnBeforeIncoming(context); } protected override bool OnBeforeOutgoing(IHubOutgoingInvokerContext context) { Debug.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub); return base.OnBeforeOutgoing(context); } }
而後在 Startup.cs 裏注入自定義的 Hub 管道模型
public void Configuration(IAppBuilder app) { GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule()); app.MapSignalR(); }
參考連接:
https://docs.microsoft.com/zh-cn/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-server