Generally if signalR is being used for a normal web client it would be seamless as both the signalR and the website would normally be hosted on the same domain, but when the client is a Cordova app, it gets tricky as it becomes a cross domain request as signalR would be hosted somewhere else like Azure cloud. To enable cross domain requests support for signalR updates we need to use a custom HubConfiguration object as they are disabled by default. In general we can do this by passing our own HubConfiguration object to the mapsignalR method which is called in the Owin start-up class to register the signalR routes as below.
1 2 3 4 5 6 7 8 9 10 |
Public class Startup { Public void Configuration(IAppBuilder app) { app.MapSignalR( new Microsoft.AspNet.SignalR.HubConfiguration { EnableJSONP = true }); } } |
But in case if we use Azure as backend for our cordova client, we would generally use Azure Mobile services or Azure app service mobile apps as it gives vast built in features right from scalability to insights for the client app. In both cases we can add signalR to the same solution.
In case of Azure mobile service to add signalR support we need to add a reference to the WindowsAzure.MobileServices.Backend.SignalR NuGet package and then call SignalRExtensionConfig.Initialize function available in the above assembly as below where the signalR routes get initialized and default configurations and the hub classes(Any class that is inherited from Hub class) are registered and you can directly build your signalR client to talk with the registered hub classes.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Public static class WebApiConfig { Public static void Register() { // Initialize SignalR SignalRExtensionConfig.Initialize(); // your default generated code goes here } } |
If observed keenly, in this case we don’t have much flexibility in registering the routes with our own HubConfiguration class where everything is taken care by the MobileService wired up events with one single function call, Also unfortunately we can’t run a custom startup class with Azure Mobile Services which rules out the possibility to add our own owin startup just like how we do in a normal website. But looking a bit closely at the Register method generated by default in a mobile service template, we can find that all dependencies needed for mobile service are being setup using one single line something like below.
1 |
HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options)) |
If we observe closely the ConfigBuilder constructor, we can find it accepts one more extra parameter that takes a function as input which is then called when the dependency engine is set up. Also the Azure Mobile Services SignalR NuGet package contains a OwinAppBuilderExtension class which can be used during start-up to extend the Owin setup for signalR. We just need to subclass it and override the ConfigureSignalR method to call the mapsignalR method and pass our own HubConfiguration class as below. Before that please refer Microsft.Owin.Cors nuget package to add CORS extensions to the Owin builder class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
public static class WebApiConfig { Public static void Register() { SignalRExtensionConfig.Initialize(); // Use this class to set configuration options for your mobile service ConfigOptions options = new ConfigOptions(); // Use this class to set WebAPI configuration options var configBuilder = new ConfigBuilder(options, (httpconfig, ioc) => { ioc.RegisterInstance(new CORSSignalROwinAppBuilderExtension(httpconfig)).As<IOwinAppBuilderExtension>(); }); HttpConfiguration config = ServiceConfig.Initialize(configBuilder); Database.SetInitializer(new SignalRWebsocketsInitializer()); } } internal class CORSSignalROwinAppBuilderExtension : OwinAppBuilderExtension { private HttpConfiguration httpconfig; public override void Configure(IAppBuilder app) { app.Map("/signalr", map => { // Setup the CORS middleware to run before SignalR. // By default this will allow all origins. You can // configure the set of origins and/or http verbs by // providing a cors options with a different policy. map.UseCors(CorsOptions.AllowAll); var hubConfiguration = new HubConfiguration { // You can enable JSONP by uncommenting line below. // JSONP requests are insecure but some older browsers (and some // versions of IE) require JSONP to work cross domain //EnableJSONP = true, }; // Run the SignalR pipeline. We're not using MapSignalR // since this branch already runs under the "/signalr" path. // hubConfiguration.EnableDetailedErrors = true; map.RunSignalR(hubConfiguration); }); base.Configure(app); } } |
But In the case of using Azure App services Mobile apps, we can directly register the routes with our custom HubConfiguration object in the Owin middleware startup function. Before this do remember to refer below signalR nuget packages to the Azure App Service Mobile apps template(It can be downloaded from Quickstart option in settings of your service from the new Azure portal), Also we need to enable web sockets and CORS for the domain from which signalR client executes. Check below code and screenshot for reference
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public partial class Startup { public static void ConfigureMobileApp(IAppBuilder app) { //Default generated code comes here app.Map("/signalr", map => { map.UseCors(CorsOptions.AllowAll); var hubConfiguration = new HubConfiguration { // You can enable JSONP by uncommenting line below. // JSONP requests are insecure but some older browsers (and some // versions of IE) require JSONP to work cross domain //EnableJSONP = true, }; map.RunSignalR(hubConfiguration); }); app.UseWebApi(config); } } |
Please note, at all the places mentioned above, EnableJSONP boolean setter is only needed if we are trying to support signalR clients using older browsers such as IE<=9, Opera<12, or Firefox<3.5. For any other new browsers (which is the case of most webkit controls in mobile apps) CORS is supported, so the function map.UseCors(CorsOptions.AllowAll) is just enough.
If we want to use JSONP, SignalR client should mention this while starting the hub connection(Refer below code). Also remember using JSONP will make SignalR to fall-back to LongPolling transport instead of WebSockets.