SignalR:在客户端和集线器之间使用基本类型消息
SignalR : Using base type messages between client and hub
我对 SignalR 还很陌生,所以我会尽可能清楚地说明 possible.Here 是我想要完成的事情:
1.Create self-hosted web application using SignalR
2.Create WPF rich client which will communicate with above-mentioned server app.
3.Use strongly typed request/response messages which inherit from base types respectively to pass data from client to server and vice-versa.
以下是我的 request/response 消息的一些定义:
//Base request message
public class HubRequestMessageBase
{
}
//Base response message
public class HubResponseMessageBase
{
public bool Success { get; set; }
public Exception Error { get; set; }
}
//Request message to query node by name
public class QueryNodeRequest : HubRequestMessageBase
{
public string Name { get; set; }
public Guid Identifier { get; set; }
}
//Response message carrying metadata for the specified node
public class QueryNodeResponse : HubResponseMessageBase
{
public NodeMetadata NodeMetadata { get; set; }
}
现在,如果我按如下方式定义我的服务器方法:
//Main method for handling client requests
public void HandleClientRequest(HubRequestMessageBase message)
{
//Omitted for brevity
}
并像这样从客户端调用服务器方法:
internal async void QueryNode(string name)
{
QueryNodeRequest req = new QueryNodeRequest();
req.Name = name;
await HubProxy.Invoke("HandleClientRequest", new object[] { req });
}
在服务器端,我仍然得到 HubRequestMessageBase 作为 message 参数类型 HandleClientRequest 方法而不是 QueryNodeRequest。现在,经过一些挖掘,我了解到 SignalR 默认情况下不处理多态性(或者消息没有得到 serialized/deserialized 正确,键入我期望- 在这种情况下 QueryNodeRequest 继承自 HubRequestMessageBase).
我的问题是:是否有可能以某种方式利用客户端和服务器上的 JsonSerializer 设置来完成此操作?请注意,我已经尝试了以下代码(以及一些变体)但没有成功(在服务器上):
GlobalHost.DependencyResolver.Register(typeof(JsonSerializer),
() => JsonSerializer.Create(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
}));
提前致谢!
干杯,
西瓦
您必须实施 IParameterResolver
and register it. The default parameter resolver 只需将 JValues
转换为目标参数类型。
我已经解决了这个问题。
问题的根本原因是信号器代码库中的这一行
https://github.com/SignalR/SignalR/blob/dev/src/Microsoft.AspNet.SignalR.Core/Json/JRawValue.cs#L28
其中 signalr 使用默认序列化器并且不允许您注入自己的序列化器。
我的解决方案是通过将我的参数类型设置为 JObject 来绕过该序列化程序
然后通过使用序列化程序的自定义实例和基于 JObject
的 JTokenReader 将该 JObject 转换为我在集线器中的具体类型
我的 Hub 方法(注意 ClientCommand 是我所有可能命令的抽象基础 class)
public async Task DispatchCommand(Envelope envelope)
{
var settings = new JsonSerializerSettings
{
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
TypeNameHandling = TypeNameHandling.Objects
};
var serializer = JsonSerializer.Create(settings);
var c = (ClientCommand) serializer.Deserialize(new JTokenReader(envelope.Command), typeof(ClientCommand));
await commandDispatcher.SendCommand(c);
}
信封类型
public sealed class Envelope
{
public JObject Command { get; set; }
}
通过在 JsonSerializer 上使用 TypeNameHandling = TypeNameHandling.Objects,我需要做的就是在我从客户端发送的对象中包含 $type 属性。
我对 SignalR 还很陌生,所以我会尽可能清楚地说明 possible.Here 是我想要完成的事情:
1.Create self-hosted web application using SignalR
2.Create WPF rich client which will communicate with above-mentioned server app.
3.Use strongly typed request/response messages which inherit from base types respectively to pass data from client to server and vice-versa.
以下是我的 request/response 消息的一些定义:
//Base request message
public class HubRequestMessageBase
{
}
//Base response message
public class HubResponseMessageBase
{
public bool Success { get; set; }
public Exception Error { get; set; }
}
//Request message to query node by name
public class QueryNodeRequest : HubRequestMessageBase
{
public string Name { get; set; }
public Guid Identifier { get; set; }
}
//Response message carrying metadata for the specified node
public class QueryNodeResponse : HubResponseMessageBase
{
public NodeMetadata NodeMetadata { get; set; }
}
现在,如果我按如下方式定义我的服务器方法:
//Main method for handling client requests
public void HandleClientRequest(HubRequestMessageBase message)
{
//Omitted for brevity
}
并像这样从客户端调用服务器方法:
internal async void QueryNode(string name)
{
QueryNodeRequest req = new QueryNodeRequest();
req.Name = name;
await HubProxy.Invoke("HandleClientRequest", new object[] { req });
}
在服务器端,我仍然得到 HubRequestMessageBase 作为 message 参数类型 HandleClientRequest 方法而不是 QueryNodeRequest。现在,经过一些挖掘,我了解到 SignalR 默认情况下不处理多态性(或者消息没有得到 serialized/deserialized 正确,键入我期望- 在这种情况下 QueryNodeRequest 继承自 HubRequestMessageBase).
我的问题是:是否有可能以某种方式利用客户端和服务器上的 JsonSerializer 设置来完成此操作?请注意,我已经尝试了以下代码(以及一些变体)但没有成功(在服务器上):
GlobalHost.DependencyResolver.Register(typeof(JsonSerializer),
() => JsonSerializer.Create(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
}));
提前致谢!
干杯, 西瓦
您必须实施 IParameterResolver
and register it. The default parameter resolver 只需将 JValues
转换为目标参数类型。
我已经解决了这个问题。
问题的根本原因是信号器代码库中的这一行 https://github.com/SignalR/SignalR/blob/dev/src/Microsoft.AspNet.SignalR.Core/Json/JRawValue.cs#L28 其中 signalr 使用默认序列化器并且不允许您注入自己的序列化器。
我的解决方案是通过将我的参数类型设置为 JObject 来绕过该序列化程序 然后通过使用序列化程序的自定义实例和基于 JObject
的 JTokenReader 将该 JObject 转换为我在集线器中的具体类型我的 Hub 方法(注意 ClientCommand 是我所有可能命令的抽象基础 class)
public async Task DispatchCommand(Envelope envelope)
{
var settings = new JsonSerializerSettings
{
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
TypeNameHandling = TypeNameHandling.Objects
};
var serializer = JsonSerializer.Create(settings);
var c = (ClientCommand) serializer.Deserialize(new JTokenReader(envelope.Command), typeof(ClientCommand));
await commandDispatcher.SendCommand(c);
}
信封类型
public sealed class Envelope
{
public JObject Command { get; set; }
}
通过在 JsonSerializer 上使用 TypeNameHandling = TypeNameHandling.Objects,我需要做的就是在我从客户端发送的对象中包含 $type 属性。