【SignalR学习系列】6. SignalR Hubs Api 详解(C# Server 端)
如何注册 SignalR 中间件
为了让客户端能够连接到 Hub ,当程序启动的时候你需要调用 MapSignalR 方法。
下面代码显示了如何在 OWIN startup 类里面定义 SignalR Hubs 路由。
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();
} }
}
The /signalr URL
默认情况下,客户端都是通过 "/signalr" 路由地址来连接到你的 Hub,你也可以修改不使用默认的 "/signalr"。
服务端代码指定Url
app.MapSignalR("/signalrTest", new HubConfiguration());
JavaScript 客户端代码指定Url
<script src="signalrTest/hubs"></script> var chat = $.connection.chatHub;
chat.url = "/signalrTest";
.NET 客户端代码指定Url
var Connection = new HubConnection("http://localhost:8080/signalrTest");
如何创建并使用 Hub 类
为了创建 Hub 类,你需要创建一个继承于 Microsoft.Aspnet.Signalr.Hub 的类
public class ContosoChatHub : Hub
{
public void NewContosoChatMessage(string name, string message)
{
Clients.All.addNewMessageToPage(name, message);
}
}
JavaScript 端的 Hub 名字
Server (驼峰命名法,第一个字母可以为大写也可以为小写)
public class ContosoChatHub : Hub
JavaScript (不管 Server 端第一个字母为大小还是小写,JavaScript 客户端必须是小写,不然就找不到对应的 Hub)
var contosoChatHubProxy = $.connection.contosoChatHub;
当使用 特性 HubName命名 Hub 的名字,那么 Server 端和 Client 端的 Hub 名大小写必须保持一致。
Server (如果 HubName 指定的第一个字母为大写,那么 JavaScript 端也必须为大写。如果是小写,那么 JavaScript 端也必须为小写)
[HubName("PascalCaseContosoChatHub")]
public class ContosoChatHub : Hub
JavaScript
var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;
强类型的 Hub
定义一个接口 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 类里定义客户端可以调用的方法
如果需要暴露一个可以在客户端调用的方法,那么需要在 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 会自动绑定复杂类型对象和数组对象。
Hub 里的方法名(非客户端方法名)
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 类中调用客户端的方法
你可以在 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 变量作为方法名
如果你需要使用一个 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");
}
如何在 Hub 类里面捕获和处理连接生命周期事件
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();
}
}
如何从 Content 里面获取 Clients 信息
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();
如何在客户端和服务端传递 State
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 类中捕获异常
- 使用 try - catch 并记录日志
- 创建一个能处理OnIncomingError的 Hubs 管道模型
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();
}
- 使用 HubException 类,抛出异常
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 类外面调用客户端方法和管理组
如果需要在 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
【SignalR学习系列】6. SignalR Hubs Api 详解(C# Server 端)的更多相关文章
- 【SignalR学习系列】8. SignalR Hubs Api 详解(.Net C# 客户端)
建立一个 SignalR 连接 var hubConnection = new HubConnection("http://www.contoso.com/"); IHubProx ...
- prometheus学习系列十一: Prometheus exporter详解
exporter详解 前面的系列中,我们在主机上面安装了node_exporter程序,该程序对外暴露一个用于获取当前监控样本数据的http的访问地址, 这个的一个程序成为exporter,Expor ...
- 【SignalR学习系列】7. SignalR Hubs Api 详解(JavaScript 客户端)
SignalR 的 generated proxy 服务端 public class ContosoChatHub : Hub { public void NewContosoChatMessage( ...
- 【HANA系列】SAP HANA XS的JavaScript API详解
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA XS的Java ...
- 【HANA系列】【第五篇】SAP HANA XS的JavaScript API详解
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列][第五篇]SAP HANA XS ...
- hibernate学习(2)——api详解对象
1 Configuration 配置对象 /详解Configuration对象 public class Configuration_test { @Test //Configuration 用户 ...
- Java8学习笔记(五)--Stream API详解[转]
为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...
- Lucene系列六:Lucene搜索详解(Lucene搜索流程详解、搜索核心API详解、基本查询详解、QueryParser详解)
一.搜索流程详解 1. 先看一下Lucene的架构图 由图可知搜索的过程如下: 用户输入搜索的关键字.对关键字进行分词.根据分词结果去索引库里面找到对应的文章id.根据文章id找到对应的文章 2. L ...
- 大数据学习笔记——Spark工作机制以及API详解
Spark工作机制以及API详解 本篇文章将会承接上篇关于如何部署Spark分布式集群的博客,会先对RDD编程中常见的API进行一个整理,接着再结合源代码以及注释详细地解读spark的作业提交流程,调 ...
随机推荐
- 如何在office2007中插入MathType教学
很多人在安装MathType数学公式编辑器时可能会遇到这个问题,MathType安装好了,可是在office2007的菜单栏中没有MathType这个选项卡,也就是说MathType没有成功加载在of ...
- Docker Hub工作流程-Docker for Web Developers(6)
在Github上创建项目仓库 和创建其他Github项目一样,在Github创建一个仓库,然后在仓库里面增加一个dockerfile,然后提交并推送到Github上. 我已经创建的仓库地址:https ...
- C#中==运算符
在这篇博客中,我们将介绍如下内容: ==运算符与基元类型 ==运算符与引用类型 ==运算符与String类型 ==运算符与值类型 ==运算符与泛型 ==运算符与基元类型 我们分别用两种方式比较两个整数 ...
- 如何在BCGControlBar界面库的CBCGPFormView子视图里面添加工具栏
最近有一个项目需求,需要在子视图里面添加一个新工具栏用来处理当前视图对应模块的操作.之前在对话框模式下做过添加工具栏的实现,在CBCGPFormView中添加工具栏还是头一次.在这里记录一下,给自己留 ...
- wildfly10报错2:ID注释有错
13:55:56,612 INFO [org.jboss.modules] (main) JBoss Modules version 1.5.1.Final 13:55:56,891 INFO [or ...
- jqueryui autocomplete的使用与angular配合的小坑
刚开始在做搜索联想功能时,使用了jquery.autocomplete.js插件,当并不理想,首先插件老旧,也只适合老版的jquery.其次在数组中只能联想到首字母一样的数据,比如[12,23,222 ...
- crontab问题处理
用pyhton写了一些爬虫,由于数据量比较大,需要跑的时间也比较长,所以将代码部署到服务器上.选择用crontab完成爬虫的定时爬取数据,这样避免了人工的干预,减少一些人为错误.但在部署crontab ...
- memcache基础
一.Memcache是一种缓存技术(内存),你可以把它想像成一张巨大的内存表,形式如下[它就是一个服务] key value key值(字符串) 可以放(字符串[二进制数据[视频.音频.图片]],数值 ...
- 学习Java绝对要懂的,Java编程中最常用的几种排序算法!
今天给大家分享一下Java中几种常见的排序算法的Java代码 推荐一下我的Java学习羊君前616,中959,最后444.把数字串联起来! ,群里有免费的学习视频和项目给大家练手.大神有空时也 ...
- Example007关闭窗口时关闭父窗口
<!--实例007关闭窗口时刷新父窗口--> <!DOCTYPE html> <html lang="en"> <head> < ...