SignalR 2.0.0 beta2 IJsonSerializer extensibility - json

I want to add some custom serialization logic so that the converted json contains camel case properties.
For that reason i tried to replace the default IJsonSerializer with one the i found in this link:
https://github.com/SignalR/SignalR/issues/500
However there seems to be a problem. More specifically, the JsonNetSerializer and IJsonSerializer classes do not exist in any of the signalR assemblies. Is there any change that happened to the recent version of signalR in that respect?

Just to clarify this a bit, as of SignalR 2 you can't replace the serializer with one that isn't from from JSON.NET. However, the JSON.NET serializer used by SinglR can be created and set using the DependacyResolver.
Here's an example where a new JsonSerializer is created to handle reference loops:
protected void Application_Start()
{
var serializerSettings = new JsonSerializerSettings();
serializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
serializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
var serializer = JsonSerializer.Create(serializerSettings);
GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => serializer);
}

In SignalR 2.0 you can't replace the JsonSerializer, there's no more IJsonSerializer abstraction. It's always JSON.NET.

Here's an example of overriding the SignalR Dependency Resolver using StructureMap.
In this particular example, I'm converting to camelCase properties and converting enums as strings.
During startup:
Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver = new StructureMapSignalRDependencyResolver();
Here's the class:
public class StructureMapSignalRDependencyResolver : Microsoft.AspNet.SignalR.DefaultDependencyResolver
{
public override object GetService(Type serviceType)
{
object service;
if (!serviceType.IsAbstract && !serviceType.IsInterface && serviceType.IsClass)
{
// Concrete type resolution
service = StructureMap.ObjectFactory.GetInstance(serviceType);
}
else
{
// Other type resolution with base fallback
service = StructureMap.ObjectFactory.TryGetInstance(serviceType) ?? base.GetService(serviceType);
}
return service;
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var objects = StructureMap.ObjectFactory.GetAllInstances(serviceType).Cast<object>();
return objects.Concat(base.GetServices(serviceType));
}
}
And StructureMap was setup with:
ObjectFactory.Configure(c =>
{
c.Scan(a =>
{
// scan the assembly that SignalR is referenced by
a.AssemblyContainingType<AppHost>();
a.WithDefaultConventions();
});
c.For<Newtonsoft.Json.JsonSerializer>()
.Singleton()
.Use(new Newtonsoft.Json.JsonSerializer
{
ContractResolver = new SignalRContractResolver(),
Converters = { new Newtonsoft.Json.Converters.StringEnumConverter() }
});
});
Here is the Contract Resolver:
public class SignalRContractResolver : Newtonsoft.Json.Serialization.IContractResolver
{
private readonly Assembly _assembly;
private readonly Newtonsoft.Json.Serialization.IContractResolver _camelCaseContractResolver;
private readonly Newtonsoft.Json.Serialization.IContractResolver _defaultContractSerializer;
public SignalRContractResolver()
{
_defaultContractSerializer = new Newtonsoft.Json.Serialization.DefaultContractResolver();
_camelCaseContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
_assembly = typeof(Connection).Assembly;
}
public Newtonsoft.Json.Serialization.JsonContract ResolveContract(Type type)
{
if (type.Assembly.Equals(_assembly))
{
return _defaultContractSerializer.ResolveContract(type);
}
return _camelCaseContractResolver.ResolveContract(type);
}
}

Related

How to use DependencyResolver in Net48 selfhosted application?

I have gotten a task that contains creating a .Net 4.8 application that contains a "HttpSelfHostServer".
I'm stuck in the quest of assigning "IServiceCollection services" to config.DependencyResolver (of type System.Web.Http.Dependencies.IDependencyResolver)
I would really like not to use autofac or other frameworks, but all guids I can find are pointing toward these frameworks. Isn't Microsoft providing a way through?
I just had to solve the same issue. This is how i did it:
First I created a new facade class to map the IServiceCollection from the host builder to the interface HttpSelfHostConfiguration supports:
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;
using Microsoft.Extensions.DependencyInjection;
namespace IntegrationReceiver.WebApi
{
public class HttpSelfHostDependencyResolver : IDependencyResolver
{
private readonly IServiceProvider sp;
private readonly IServiceScope scope;
public HttpSelfHostDependencyResolver(IServiceProvider sp)
{
this.sp = sp;
this.scope = null;
}
public HttpSelfHostDependencyResolver(IServiceScope scope)
{
this.sp = scope.ServiceProvider;
this.scope = scope;
}
public IDependencyScope BeginScope() => new HttpSelfHostDependencyResolver(sp.CreateScope());
public void Dispose() => scope?.Dispose();
public object GetService(Type serviceType) => sp.GetService(serviceType);
public IEnumerable<object> GetServices(Type serviceType) => sp.GetServices(serviceType);
}
}
This required me to get the latest NuGet package Microsoft.Extensions.DependencyInjection.Abstractions according to an answer here: How do I see all services that a .NET IServiceProvider can provide?
I then registered my HttpSelfHostServer in the service provider with this code:
services.AddSingleton(sp => new HttpSelfHostDependencyResolver(sp));
services.AddSingleton(sp =>
{
//Starting the HttpSelfHostServer with user-level permissions requires to first run a command like
// netsh http add urlacl url=http://+:8080/ user=[DOMAINNAME]\[USERNAME]
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
config.DependencyResolver = sp.GetRequiredService<HttpSelfHostDependencyResolver>();
return new HttpSelfHostServer(config);
});
And finally, to find my ApiController, I had to register that too in the service provider. I did that simply with:
services.AddScoped<HealthCheckController>();
For brewity, I'm just including my api controller below to illustrate how it now gets its dependencies:
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
namespace IntegrationReceiver.WebApi
{
public class HealthCheckController : ApiController
{
private readonly ServiceBusRunner serviceBusRunner;
public HealthCheckController(ServiceBusRunner serviceBusRunner)
{
this.serviceBusRunner = serviceBusRunner;
}
[HttpGet]
public async Task<HttpResponseMessage> Get()
{
var response = new
{
serviceBusRunner.RunningTasks,
serviceBusRunner.MaxRunningTasks
};
return await Json(response)
.ExecuteAsync(System.Threading.CancellationToken.None);
}
}
}
This is a pretty dumb-down implementation but works for me until I can upgrade this code to net5.
I hope it helps you too!

Custom resolver with UTF8JSON

I did search on So but it looks like for this type of json serializer there isnt much info out there. I'm using original version since I need 4.5 NET target: https://github.com/neuecc/Utf8Json
I have a custom object that needs custom serialization/deserialization logic:
public class CustomInstanceFormatter : IJsonFormatter<CustomInstance>
{
public void Serialize(ref JsonWriter writer, CustomInstance value, IJsonFormatterResolver formatterResolver)
{
if (value == null) { writer.WriteNull(); return; }
writer.WriteString(value.name);
formatterResolver.GetFormatterWithVerify<SomeNamespace.Data>().Serialize(ref writer, value.color, formatterResolver);
}
public CustomInstance Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
{
if (reader.ReadIsNull()) return null;
var inst = new CustomInstance(reader.ReadString());
inst.color = formatterResolver.GetFormatterWithVerify<SomeNamespace.Data>().Deserialize(ref reader, formatterResolver);
return inst;
}
}
My problem is that the short readme does not explain anywhere how can I consume it. With NewtonSoft JSON it is fairly simple, but here I'm at a loss. JsonSerializer in both of his methods for Serialize/Deserialize only accepts a IJsonResolver.
You have to create your own Resolver which consumes your Formatter like this:
public class StandardFunctionResolver : IJsonFormatterResolver
{
public static StandardFunctionResolver Instance = new StandardFunctionResolver();
public Dictionary<Type, IJsonFormatter> formatters;
private StandardFunctionResolver ()
{
formatters = new Dictionary<Type, IJsonFormatter>()
{
{typeof(CustomInstance), new CustomInstanceFormatter()},
};
}
public IJsonFormatter<T> GetFormatter<T>()
{
if (formatters.TryGetValue(typeof(T), out var typeFormatter))
{
return (IJsonFormatter<T>)typeFormatter;
}
return StandardResolver.Default.GetFormatter<T>();
}
}
and then let UTF8Json consume it on Serialization or Deserialization like:
var idk = Utf8Json.JsonSerializer.ToJsonString(obj, StandardFunctionResolver.Instance);

SignalR, WebApi, Autofac, camelCasedJson

I Have a web api app, inluding some SignalR functionality that has it's dependencies managed by Autofac.
SignalR, out of the box, does not support camelCased Json properties.
There's a decent fix here, but I need to integrate it with Autofac.
So.....
Here's the SignalR hub
public class PledgeHub : Hub
{
public void SendPledge(LivePledgeUpdate pledge)
{
Clients.All.sendPledge(pledge);
}
}
Here's the startup.cs class inluding the Camelcased Resolver from the article
[assembly: OwinStartup(typeof(Startup))]
namespace Filanthropy.Web
{
public partial class Startup
{
private readonly HttpConfiguration config;
public Startup()
{
config = new HttpConfiguration();
}
public void Configuration(IAppBuilder app)
{
// Set the dependency resolver to be Autofac.
var container = BuildContainer(config);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
ConfigureAuth(app);
ConfigureWebApi(config);
app.MapSignalR();
app.UseWebApi(config);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
}
private void ConfigureWebApi(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
private static IContainer BuildContainer(HttpConfiguration config)
{
var builder = new ContainerBuilder();
// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL: Register the Autofac filter provider.
builder.RegisterWebApiFilterProvider(config);
builder.RegisterType<PledgeHub>().ExternallyOwned();
GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => JsonSerializerFactory.Value);
return builder.Build();
}
private static readonly Lazy<JsonSerializer> JsonSerializerFactory = new Lazy<JsonSerializer>(GetJsonSerializer);
private static JsonSerializer GetJsonSerializer()
{
return new JsonSerializer
{
ContractResolver = new FilteredCamelCasePropertyNamesContractResolver
{
TypesToInclude =
{
typeof(SignalRHubs.PledgeHub),
}
}
};
}
}
}
I get camelCased properties just fine with everything that's served via WebApi, but not via Signalr.
I'm guessing that your problem is this:
GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => JsonSerializerFactory.Value);
If you're using Autofac as the container / dependency resolver, you want to register the serializer with Autofac, not with the soon-to-be-replaced global dependency resolver.
builder.Register(ctx => JsonSerializerFactory.Value).As<JsonSerializer>();
Also, I see you're using OWIN. When you use OWIN, you don't reference GlobalHost - you set up your hub configuration separately. The docs have an example showing how to do it:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// STANDARD SIGNALR SETUP:
// Get your HubConfiguration. In OWIN, you'll create one
// rather than using GlobalHost.
var config = new HubConfiguration();
// Register your SignalR hubs.
builder.RegisterHubs(Assembly.GetExecutingAssembly());
// Set the dependency resolver to be Autofac.
var container = builder.Build();
config.Resolver = new AutofacDependencyResolver(container);
// OWIN SIGNALR SETUP:
// Register the Autofac middleware FIRST, then the standard SignalR middleware.
app.UseAutofacMiddleware(container);
app.MapSignalR("/signalr", config);
}
}

How can I do JSON serializer ignore navigation properties?

I am exactly in the same case that this question:
How do I make JSON.NET ignore object relationships?
I see the proposed solution and I know I must use a Contract Revolver, and I also see the code of the Contract Resolver, but I do not know how to use it.
Should I use it in the WebApiConfig.vb?
Should I modify my Entity Model anyway?
It is a useful question👍 and I hope this help:
A)
If you have created your models manually (without Entity Framework), mark the relation properties as virtual first.
If your models were created by EF, It has already done it for you and each Relation Property is marked as virtual, as seen below:
Sample class:
public class PC
{
public int FileFolderId {get;set;}
public virtual ICollection<string> Libs { get; set; }
public virtual ICollection<string> Books { get; set; }
public virtual ICollection<string> Files { get; set; }
}
B)
Those relation properties can now be ignored by the JSON serializer by using the following ContractResolver for JSON.NET:
CustomResolver:
class CustomResolver : DefaultContractResolver
{
private readonly List<string> _namesOfVirtualPropsToKeep=new List<string>(new String[]{});
public CustomResolver(){}
public CustomResolver(IEnumerable<string> namesOfVirtualPropsToKeep)
{
this._namesOfVirtualPropsToKeep = namesOfVirtualPropsToKeep.Select(x=>x.ToLower()).ToList();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
var propInfo = member as PropertyInfo;
if (propInfo != null)
{
if (propInfo.GetMethod.IsVirtual && !propInfo.GetMethod.IsFinal
&& !_namesOfVirtualPropsToKeep.Contains(propInfo.Name.ToLower()))
{
prop.ShouldSerialize = obj => false;
}
}
return prop;
}
}
C)
Finally, to serialize your model easily use the above ContractResolver. Set it up like this:
// -------------------------------------------------------------------
// Serializer settings
JsonSerializerSettings settings = new JsonSerializerSettings
{
// ContractResolver = new CustomResolver();
// OR:
ContractResolver = new CustomResolver(new []
{
nameof(PC.Libs), // keep Libs property among virtual properties
nameof(PC.Files) // keep Files property among virtual properties
}),
PreserveReferencesHandling = PreserveReferencesHandling.None,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented
};
// -------------------------------------------------------------------
// Do the serialization and output to the console
var json = JsonConvert.SerializeObject(new PC(), settings);
Console.WriteLine(json);
// -------------------------------------------------------------------
// We can see that "Books" filed is ignored in the output:
// {
// "FileFolderId": 0,
// "Libs": null,
// "Files": null
// }
Now, all the navigation (relation) properties [virtual properties] will be ignored automatically except you keep some of them by determine them in your code.😎
Live DEMO
Thanks from #BrianRogers for his answer here.
If you are using Newtonsoft.Json
Mark field with
Newtonsoft.Json.JsonIgnore
Instead of
System.Text.Json.Serialization.JsonIgnore

How can I do dependency injection into action filters in ASP.NET 4 RC WebAPI?

I'm using Windsor to manage IoC for my controllers in a WebAPI project. I've got a DependencyResolver working nicely to resolve controller dependencies, but now I'm looking to inject dependencies into a custom action filter I'm using to manage authentication.
I've looked into using a custom ActionInvoker but it's not clear from the interface that WebAPI is using how I would go about resolving property dependencies on the custom action filter attribute before it executes. Anyone have a good example of how to do this in the MVC 4 RC?
EDIT: I'm aware you can't do constructor injection on filters, because they're attributes and therefore instantiated by the .NET framework - but I'm hoping there's some point in the execution lifecycle that happens AFTER the filter is instantiated but BEFORE it gets executed, where I could run some custom code to enumerate across the filters' public properties and inject the necessary services.
Action filters are attributes. In .NET attribute the instantiation process is managed by the .NET runtime and you don't have control over it. So one possibility is to use Poor Man's Dependency Injection which I would personally advice you against.
Another possibility is to use a marker attribute:
public class MyActionFilterAttribute : Attribute
{
}
and then have the action filter using constructor injection:
public class MyActionFilter : ActionFilterAttribute
{
private readonly IFoo _foo;
public MyActionFilter(IFoo foo)
{
_foo = foo;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ActionDescriptor.GetCustomAttributes<MyActionFilterAttribute>().Any())
{
// The action is decorated with the marker attribute =>
// do something with _foo
}
}
}
and then register it as a global action filter in Application_Start:
IFoo foo = ....
GlobalConfiguration.Configuration.Filters.Add(new MyActionFilter(foo));
I had the same problem, but decided to go for the ServiceLocator (DependencyResolver.GetService) for this, as its in the framework it seems to me to be a valid approach
public class RequiresSessionAttribute :
ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var sessionService =
(ISessionService) actionContext
.ControllerContext.Configuration.DependencyResolver
.GetService(typeof (ISessionService));
var sessionId = HttpUtility
.ParseQueryString(actionContext.Request.RequestUri.Query)
.Get("sessionId");
if (sessionId == null
|| !sessionService.IsValid(sessionId))
throw new SessionException();
base.OnActionExecuting(actionContext);
}
}
and here is a test for this attribute, bit of a pain but possible
public class requires_sessionId
{
[Fact]
void can_call_action_with_session_id()
{
var context = GetContext("http://example.com/?sessionId=blaa");
var sut = new RequiresSessionAttribute();
Assert.DoesNotThrow(
() => sut.OnActionExecuting(context));
}
[Fact]
void can_not_call_action_without_session_id()
{
var context = GetContext("http://example.com/");
var sut = new RequiresSessionAttribute();
Assert.Throws<SessionException>(
() => sut.OnActionExecuting(context));
}
HttpActionContext GetContext(string url)
{
var sessionServiceMock = new Mock<ISessionService>();
sessionServiceMock
.Setup(x => x.IsValid(It.IsAny<string>()))
.Returns(true);
var dependancyResolverMock = new Mock<IDependencyResolver>();
dependancyResolverMock
.Setup(x => x.GetService(It.IsAny<Type>()))
.Returns(sessionServiceMock.Object);
var config = new HttpConfiguration
{
DependencyResolver = dependancyResolverMock.Object
};
var controllerContext = new HttpControllerContext
{
Configuration = config,
Request = new HttpRequestMessage(
HttpMethod.Get,
url)
};
return
new HttpActionContext
{
ControllerContext = controllerContext,
};
}
}