c# – SignalR Typenamehandling

前端之家收集整理的这篇文章主要介绍了c# – SignalR Typenamehandling前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我试图让SignalR使用自定义JsonSerializerSettings作为其有效负载,特别是我试图设置TypeNameHandling = TypeNameHandling.Auto.

问题似乎是,SignalR使用hubConnection.JsonSerializer和GlobalHost.DependencyResolver.Resolve< JsonSerializer>()中的设置作为其内部数据结构,然后导致各种破坏(当我设置TypeNameHandling.All时内部服务器崩溃)作为最粗鲁的例子,但是使用TypeNameHandling.Auto我也会遇到问题,特别是涉及IProgress<>回调时).

是否有任何解决方法或我只是做错了?

示例代码演示:

服务器:

class Program
{
    static void Main(string[] args)
    {
        using (WebApp.Start("http://localhost:8080"))
        {
            Console.ReadLine();
        }
    }
}

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var hubConfig = new HubConfiguration()
        {
            EnableDetailedErrors = true
        };
        GlobalHost.DependencyResolver.Register(typeof(JsonSerializer),ConverterSettings.GetSerializer);
        app.MapSignalR(hubConfig);
    }
}

public interface IFoo
{
    string Val { get; set; }
}
public class Foo : IFoo
{
    public string Val { get; set; }
}

public class MyHub : Hub
{
    public IFoo Send()
    {
        return new Foo { Val = "Hello World" };
    }
}

客户:

class Program
{
    static void Main(string[] args)
    {
        Task.Run(async () => await Start()).Wait();
    }

    public static async Task Start()
    {
        var hubConnection = new HubConnection("http://localhost:8080");
        hubConnection.JsonSerializer = ConverterSettings.GetSerializer();
        var proxy = hubConnection.CreateHubProxy("MyHub");
        await hubConnection.Start();
        var result = await proxy.Invoke<IFoo>("Send");
        Console.WriteLine(result.GetType());
    }

共享:

public static class ConverterSettings
{
    public static JsonSerializer GetSerializer()
    {
        return JsonSerializer.Create(new JsonSerializerSettings()
        {
            TypeNameHandling = TypeNameHandling.All
        });
    }
}

解决方法

这可以通过利用您的类型和SignalR类型在不同的 assemblies中的事实来实现.想法是创建适用于程序集中所有类型的 JsonConverter.当在对象图中首次遇到来自其中一个程序集的类型(可能作为根对象)时,转换器将临时设置 jsonSerializer.TypeNameHandling = TypeNameHandling.Auto,然后继续执行该类型的标准序列化,在此期间禁用自身以防止无限递归:
public class PolymorphicAssemblyRootConverter : JsonConverter
{
    [ThreadStatic]
    static bool disabled;

    // Disables the converter in a thread-safe manner.
    bool Disabled { get { return disabled; } set { disabled = value; } }

    public override bool CanWrite { get { return !Disabled; } }

    public override bool CanRead { get { return !Disabled; } }

    readonly HashSet<Assembly> assemblies;

    public PolymorphicAssemblyRootConverter(IEnumerable<Assembly> assemblies)
    {
        if (assemblies == null)
            throw new ArgumentNullException();
        this.assemblies = new HashSet<Assembly>(assemblies);
    }

    public override bool CanConvert(Type objectType)
    {
        return assemblies.Contains(objectType.Assembly);
    }

    public override object ReadJson(JsonReader reader,Type objectType,object existingValue,JsonSerializer serializer)
    {
        using (new PushValue<bool>(true,() => Disabled,val => Disabled = val)) // Prevent infinite recursion of converters
        using (new PushValue<TypeNameHandling>(TypeNameHandling.Auto,() => serializer.TypeNameHandling,val => serializer.TypeNameHandling = val))
        {
            return serializer.Deserialize(reader,objectType);
        }
    }

    public override void WriteJson(JsonWriter writer,object value,val => serializer.TypeNameHandling = val))
        {
            // Force the $type to be written unconditionally by passing typeof(object) as the type being serialized.
            serializer.Serialize(writer,value,typeof(object));
        }
    }
}

public struct PushValue<T> : IDisposable
{
    Action<T> setValue;
    T oldValue;

    public PushValue(T value,Func<T> getValue,Action<T> setValue)
    {
        if (getValue == null || setValue == null)
            throw new ArgumentNullException();
        this.setValue = setValue;
        this.oldValue = getValue();
        setValue(value);
    }

    #region IDisposable Members

    // By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
    public void Dispose()
    {
        if (setValue != null)
            setValue(oldValue);
    }

    #endregion
}

然后在启动时,您将此转换器添加到默认的JsonSerializer,传入要应用“$type”的程序集.

更新

如果由于某种原因在启动时传递程序集列表不方便,则可以在objectType.Namespace之前启用转换器.生成在指定命名空间中的所有类型将自动使用TypeNameHandling.Auto进行序列化.

或者,您可以引入一个属性,targets是一个程序集,类或接口,并在与适当的转换器结合使用时启用TypeNameHandling.Auto:

public class EnableJsonTypeNameHandlingConverter : JsonConverter
{
    [ThreadStatic]
    static bool disabled;

    // Disables the converter in a thread-safe manner.
    bool Disabled { get { return disabled; } set { disabled = value; } }

    public override bool CanWrite { get { return !Disabled; } }

    public override bool CanRead { get { return !Disabled; } }

    public override bool CanConvert(Type objectType)
    {
        if (Disabled)
            return false;
        if (objectType.Assembly.GetCustomAttributes<EnableJsonTypeNameHandlingAttribute>().Any())
            return true;
        if (objectType.GetCustomAttributes<EnableJsonTypeNameHandlingAttribute>(true).Any())
            return true;
        foreach (var type in objectType.GetInterfaces())
            if (type.GetCustomAttributes<EnableJsonTypeNameHandlingAttribute>(true).Any())
                return true;
        return false;
    }

    public override object ReadJson(JsonReader reader,typeof(object));
        }
    }
}

[System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Class | System.AttributeTargets.Interface)]
public class EnableJsonTypeNameHandlingAttribute : System.Attribute
{
    public EnableJsonTypeNameHandlingAttribute()
    {
    }
}

注意 – 使用各种测试用例但不测试SignalR本身,因为我目前没有安装它.

TypeNameHandling注意

使用TypeNameHandling时,请注意Newtonsoft docs中的此注意事项:

TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.

有关可能需要的原因的讨论,请参阅TypeNameHandling caution in Newtonsoft Json.

猜你在找的C#相关文章