我尝试使用此处EnableJsonTypeNameHandlingConverter
发布的客户端和服务器序列化配置以及以下用于双向连接的客户端和服务器代码来更改客户端和服务器序列化配置。
如您所见,有代码可以在客户端和服务器上设置自定义序列化......但它不起作用!
using System;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Client;
using Newtonsoft.Json;
using Owin;
class Program
{
static void Main(string[] args)
{
// Ensure serialization and deserialization works outside SignalR
INameAndId nameId = new NameAndId(5, "Five");
string json = JsonConvert.SerializeObject(nameId, Formatting.Indented, new EnableJsonTypeNameHandlingConverter());
var clone = JsonConvert.DeserializeObject(json, typeof(INameAndId), new EnableJsonTypeNameHandlingConverter());
Console.WriteLine(json);
// Start server
// http://+:80/Temporary_Listen_Addresses is allowed by default - all other routes require special permission
string url = "http://+:80/Temporary_Listen_Addresses/example";
using (Microsoft.Owin.Hosting.WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
// Start client side
HubConnection conn = new HubConnection("http://127.0.0.1:80/Temporary_Listen_Addresses/example");
conn.JsonSerializer.Converters.Add(new EnableJsonTypeNameHandlingConverter());
// Note: SignalR requires CreateHubProxy() to be called before Start()
var hp = conn.CreateHubProxy(nameof(SignalRHub));
var proxy = new SignalRProxy(hp, new SignalRCallback());
conn.Start().Wait();
proxy.Foo();
// AggregateException on server: Could not create an instance of type
// SignalRSelfHost.INameAndId. Type is an interface or abstract class
// and cannot be instantiated.
proxy.Bar(nameId);
Console.ReadLine();
}
}
}
class Startup
{
// Magic method expected by OWIN
public void Configuration(IAppBuilder app)
{
//app.UseCors(CorsOptions.AllowAll);
var hubCfg = new HubConfiguration();
var jsonSettings = new JsonSerializerSettings();
jsonSettings.Converters.Add(new EnableJsonTypeNameHandlingConverter());
hubCfg.EnableDetailedErrors = true;
hubCfg.Resolver.Register(typeof(JsonSerializer), () => JsonSerializer.Create(jsonSettings));
GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => JsonSerializer.Create(jsonSettings));
app.MapSignalR(hubCfg);
}
}
// Messages that can be sent to the server
public interface ISignalRInterface
{
void Foo();
void Bar(INameAndId param);
}
// Messages that can be sent back to the client
public interface ISignalRCallback
{
void Baz();
}
// Server-side hub
public class SignalRHub : Hub<ISignalRCallback>, ISignalRInterface
{
protected ISignalRCallback GetCallback(string hubname)
{
// Note: SignalR hubs are transient - they connection lives longer than the
// Hub - so it is generally unwise to store information in member variables.
// Therefore, the ISignalRCallback object is not cached.
return GlobalHost.ConnectionManager.GetHubContext<ISignalRCallback>(hubname).Clients.Client(Context.ConnectionId);
}
public virtual void Foo() { Console.WriteLine("Foo!"); }
public virtual void Bar(INameAndId param) { Console.WriteLine("Bar!"); }
}
// Client-side proxy for server-side hub
public class SignalRProxy
{
private IHubProxy _Proxy;
public SignalRProxy(IHubProxy proxy, ISignalRCallback callback)
{
_Proxy = proxy;
_Proxy.On(nameof(ISignalRCallback.Baz), callback.Baz);
}
public void Send(string method, params object[] args)
{
_Proxy.Invoke(method, args).Wait();
}
public void Foo() => Send(nameof(Foo));
public void Bar(INameAndId param) => Send(nameof(Bar), param);
}
public class SignalRCallback : ISignalRCallback
{
public void Baz() { }
}
[Serializable]
public class NameAndId : INameAndId
{
public NameAndId(int id, string name)
{
Id = id;
Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
}
[EnableJsonTypeNameHandling]
public interface INameAndId
{
string Name { get; }
int Id { get; }
}
SignalR 调用传递给的 lambdaGlobalHost.DependencyResolver
不少于 8 次,但最终它忽略了提供的序列化程序。
我找不到任何关于 SignalR 参数序列化的文档,所以我使用 Rider 的反编译调试器来帮助找出发生了什么。
在 SignalR 中有一个HubRequestParser.Parse
使用正确的方法JsonSerializer
,但它实际上并没有反序列化参数。稍后将反序列化参数,DefaultParameterResolver.ResolveParameter()
其中间接调用CreateDefaultSerializerSettings()
以下调用堆栈:
JsonUtility.CreateDefaultSerializerSettings() in Microsoft.AspNet.SignalR.Json, Microsoft.AspNet.SignalR.Core.dll
JsonUtility.CreateDefaultSerializer() in Microsoft.AspNet.SignalR.Json, Microsoft.AspNet.SignalR.Core.dll
JRawValue.ConvertTo() in Microsoft.AspNet.SignalR.Json, Microsoft.AspNet.SignalR.Core.dll
DefaultParameterResolver.ResolveParameter() in Microsoft.AspNet.SignalR.Hubs, Microsoft.AspNet.SignalR.Core.dll
Enumerable.<ZipIterator>d__61<ParameterDescriptor, IJsonValue, object>.MoveNext() in System.Linq, System.Core.dll
new Buffer<object>() in System.Linq, System.Core.dll
Enumerable.ToArray<object>() in System.Linq, System.Core.dll
DefaultParameterResolver.ResolveMethodParameters() in Microsoft.AspNet.SignalR.Hubs, Microsoft.AspNet.SignalR.Core.dll
HubDispatcher.InvokeHubPipeline() in Microsoft.AspNet.SignalR.Hubs, Microsoft.AspNet.SignalR.Core.dll
HubDispatcher.OnReceived() in Microsoft.AspNet.SignalR.Hubs, Microsoft.AspNet.SignalR.Core.dll
PersistentConnection.<>c__DisplayClass64_1.<ProcessRequestPostGroupRead>b__5() in Microsoft.AspNet.SignalR, Microsoft.AspNet.SignalR.Core.dll
TaskAsyncHelper.FromMethod() in Microsoft.AspNet.SignalR, Microsoft.AspNet.SignalR.Core.dll
PersistentConnection.<>c__DisplayClass64_0.<ProcessRequestPostGroupRead>b__4() in Microsoft.AspNet.SignalR, Microsoft.AspNet.SignalR.Core.dll
WebSocketTransport.OnMessage() in Microsoft.AspNet.SignalR.Transports, Microsoft.AspNet.SignalR.Core.dll
DefaultWebSocketHandler.OnMessage() in Microsoft.AspNet.SignalR.WebSockets, Microsoft.AspNet.SignalR.Core.dll
WebSocketHandler.<ProcessWebSocketRequestAsync>d__25.MoveNext() in Microsoft.AspNet.SignalR.WebSockets, Microsoft.AspNet.SignalR.Core.dll
AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext() in System.Runtime.CompilerServices, mscorlib.dll [5]
ExecutionContext.RunInternal() in System.Threading, mscorlib.dll [5]
ExecutionContext.Run() in System.Threading, mscorlib.dll [5]
AsyncMethodBuilderCore.MoveNextRunner.Run() in System.Runtime.CompilerServices, mscorlib.dll [5]
...
在SignalR 源代码中,问题很明显:
// in DefaultParameterResolver
public virtual object ResolveParameter(ParameterDescriptor descriptor, IJsonValue value)
{
// [...]
return value.ConvertTo(descriptor.ParameterType);
}
// in JRawValue
public object ConvertTo(Type type)
{
// A non generic implementation of ToObject<T> on JToken
using (var jsonReader = new StringReader(_value))
{
var serializer = JsonUtility.CreateDefaultSerializer();
return serializer.Deserialize(jsonReader, type);
}
}
// in JsonUtility
public static JsonSerializer CreateDefaultSerializer()
{
return JsonSerializer.Create(CreateDefaultSerializerSettings());
}
public static JsonSerializerSettings CreateDefaultSerializerSettings()
{
return new JsonSerializerSettings() { MaxDepth = DefaultMaxDepth };
}
因此 SignalR 将您的自定义(反)序列化器用于其部分工作,而不是用于参数反序列化。
我想不通的是,2015 年对另一个问题的回答有 8 票,这似乎意味着该解决方案在过去 4 年中的某个时候对某人有效,但如果是这样,那么一定有一个技巧我们不知道。
也许.NET Core 版本的 SignalR解决了这个问题。看起来该版本已被大幅重构,不再有DefaultParameterResolver.cs
文件。有人关心检查吗?