Cannot pass a DependencyResolver to MapSignalR in a HubConfiguration - signalr

I have a MVC 4 app where I'm using SignalR and a dependency injection framework. Thus, I needed to implement a custom DependencyResolver. In order to wire it up, I'm using
var resolver = new CustomSignalRDependencyResolver(...);
GlobalHost.DependencyResolver = resolver;
app.MapSignalR();
and everything works as expected. However, if I use a HubConfiguration object like this:
var resolver = new CustomSignalRDependencyResolver(...);
app.MapSignalR(new HubConfiguration(
{
Resolver = resolver
});
everything seems to work (the objects are correctly wired-up, there are no errors or warning whatsoever) but the remote methods are not invoked anymore. Can someone explain the difference between the first and the second approach?

I think Lars Höppner (the user who left the first comment on this post) is right.
You can definitely use a custom dependency resolver without changing GlobalHost.DependencyResolver (the SignalR test suite does this quite often), but you have to be sure GlobalHost doesn't show up anywhere else in your code.
This means that absolutely no references to:
GlobalHost.Configuration
GlobalHost.ConnectionManager
GlobalHost.TraceManager
GlobalHost.HubPipeline
and of course GlobalHost.DependencyResolver
The SO post Lars linked to (SignalR: Sending data using GlobalHost.ConnectionManager not working) shows you how to get the ConnectionManager from your custom dependency resolver; you can do the same thing for all the other properties on GlobalHost.

I'm assuming you're performing the second approach yet still trying to use the GlobalHost dependency resolver.
If you wire your custom dependency resolver up via your second approach the GlobalHost's dependency resolver will not be the same as the one you pass into your custom hub configuration.
Therefore in order to still use the GlobalHost object you'll need to do your second approach AND set the GlobalHost dependency resolver:
var resolver = new CustomSignalRDependencyResolver(...);
app.MapSignalR(new HubConfiguration(
{
Resolver = resolver
});
GlobalHost.DependencyResolver = resolver;
Hope this helps!

Related

Service Fabric Web API Versioning issue

I'm working on a service fabric project with multiple stateless services. When i try to add versioning as in the code below
[Authorize]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class SessionController : Controller
{
...
}
it's not working when calling the service later using Postman or using some client winforms app i made just to call this service. And when i say it's not working i mean it's not looking for a specific version i placed in the controller.
e.g.
I'm calling http://localhost:1234/api/v1.0/session/set-session and as you can see in my controller i only have version 2.0. Now my API gets hit this way or another no matter what version number i put in.
I added code to the Startup.cs
services.AddApiVersioning(options => {
options.DefaultApiVersion = new ApiVersion(2, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new HeaderApiVersionReader("x-api-version");
});
Specific API call looks like this:
[HttpPost]
[Route("set-session")]
public async Task<IActionResult> SetSession([FromBody] SessionModel model)
{ ... }
Can anyone tell me what am i missing or maybe api versioning is not supported in service fabric at all?
Thanks.
Does your solution work locally? Based on what I see, I would suspect - no. This should have nothing to do with Service Fabric at all.
Issue 1
I see that your base class inherits from Controller, which is allowed, but is usually ControllerBase. No concern there, just FYI. The crux of the problem is likely that your controller has not applied the [ApiController] attribute. API Versioning defines IApiControllerSpecification and IApiControllerFilter, which is used to filter which controllers should be considered an API. This is important for developers building applications that have the UI and API parts mixed. A controller is a controller in ASP.NET Core and it was difficult to distinguish these two in the early days. There is now a built-in IApiControllerSpecification that considers any controller with [ApiController] applied to be an API. This can be changed, replaced, or completely disabled using ApiVersioningOptions.UseApiBehavior = false.
If your library/application is only APIs, you can decorate all controllers at once using:
[assembly: ApiController]
Since your controller is not currently being considered an API, all requests matching the route are being directed there. The value 1.0 is being considered an arbitrary string rather than an API version. This is why it matches at all instead of HTTP 400. I suspect you must only have one API and it is defined as 2.0; otherwise, I would expect an AmbiguousActionException.
Issue 2
Your example shows that you are trying to version by URL segment, but you've configured the options to only consider the header x-api-version. This option should be configured with one of the following:
URL Segment (only)
options.ApiVersionReader = new UrlSegmentApiVersionReader();
URL Segment and Header
// registration order is irrelevant
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("x-api-version"));
Default (Query String and URL Segment)
// NOTE: this is the configuration
// options.ApiVersionReader = ApiVersionReader.Combine(
// new QueryStringApiVersionReader(),
// new UrlSegmentApiVersionReader());
Side Note
As defined, using the URL segment and header versioning methodologies don't make sense. You have a single route which requires an API version. A client will always have to include the API version in every request so there is no point to also supporting a header.
If you define 2 routes, then it makes sense:
[Route("api/[controller]")] // match by header
[Route("api/v{version:apiVersion}/[controller]")] // match by url segment
Versioning by URL segment, while common, is the least RESTful. It violates the Uniform Interface constraint. This issue demonstrates yet another problem with that approach. Query string, header, media type, or any combination thereof will all work with the single route template of: [Route("api/[controller]")]
Observation 1
You have configured options.AssumeDefaultVersionWhenUnspecified = true. This will have no effect when versioning by URL segment. It is impossible to provide a default value of route parameter in the middle of a template. The same would be true for api/value/{id}/subvalues if {id} is not specified.
This option will have an effect if you:
Add a second route template that doesn't have the API version parameter
You update your versioning strategy to not use a URL segment
It should be noted that is a highly abused feature. It is meant to grandfather in existing services that didn't previously have explicit versioning because adding it will break existing clients. You should be cognizant of that if that isn't your use case.

How to read ASP.NET Session on IdentityServer3? OWIN Pipeline Order is broken

Problem: I'm trying to use ASP.NET Session on IdentityServer3 Controllers, but I just can't make it work.
I found this similar question that explains how to enable session on OWIN middleware, and it worked perfectly: I created some controllers outside of IdentityServer (that is, outside of the pipeline Mapped in "/core") and it worked perfectly. I also added Autofac to my controllers to make sure that Autofac (used extensivly in IdentityServer) was not the problem, and it works fine.
This is working code - Startup.cs:
// register middleware that enables session using SetSessionStateBehavior(SessionStateBehavior.Required)
app.RequireAspNetSession();
var config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<DummyClassCreatedByAutofac>();;
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
//app.UseIdentityServer(); // if I add IdentityServer, context.Session is always null
This works fine. The middleware (SetSessionStateBehavior) runs on every requisition, and the session is available on the constructor. However, as soon as I add app.UseIdentityServer() to my Startup.cs, IdentityServer starts working perfectly (and all requisitions pass through the session middleware) but now Session is null for all controllers - not only for IdentityServer controllers but also for my other controllers (which are outside of IdentityServer pipeline) - they stop working.
If I completely remove all my code and just stick with regular IdentityServer pipeline, I just can't find the correct place to add RequireAspNetSession() in the pipeline. I tried adding in many different places, but couldn't make it work.
After some trial-and-error, I realized that the session middleware RequireAspNetSession() was being broken by the following two calls: app.UseEmbeddedFileServer() and app.ConfigureCookieAuthentication. When both calls were removed, Session was available to IdentityServer controllers (and also were available again to my other controllers). Probably the problem is because some of those OWIN middlewares run on specific stages (using UseStageMarker and defining a PipelineStage), and probably I'm breaking the priority order of the middlewares. UseEmbeddedFileServer for example runs on stage PipelineStage.MapHandler, as well as
RequireAspNetSession.
I tried adding app.RequireAspNetSession() both before and after those middlewares, but it didn't work either. In fact, app.RequireAspNetSession() never works when it is used inside Map() method, which is where those middlewares are configured (inside UseIdentityServer()):
app.Map("/core", coreApp =>
{
// this DOESN'T work, even if I remove
// UseEmbeddedFileServer() or ConfigureCookieAuthentication()
coreApp.RequireAspNetSession();
//...
coreApp.UseIdentityServer(idsrvOptions);
}
_
// this WORKS as long as inside the Map() I don't call
// UseEmbeddedFileServer() or ConfigureCookieAuthentication()
app.RequireAspNetSession();
app.Map("/core", coreApp =>
{
//...
coreApp.UseIdentityServer(idsrvOptions);
}
Last, if I don't use the Map method (and setup the IdentityServer API directly on root folder), it works fine (session is available to all controllers, even if I keep the UseEmbeddedFileServer() and ConfigureCookieAuthentication() middlewares). But it's not acceptable because I need to run APIs on a mapped folder.
In summary: If I use RequireAspNetSession() inside Map(), it doesn't work (session is always null). If I use RequireAspNetSession() outside Map() but keep the UseEmbeddedFileServer() or ConfigureCookieAuthentication() inside the Map(), it also doesn't work.
How to make the RequireAspNetSession() work in IdentityServer3 pipeline?
Also, how could Map("/core") affect (and broke) the pipeline for my DefaultController that is hosted outside of that pipeline?
It is not broken. It is by design. IdentityServer clears session on purpose.
You have a couple alternatives.
1) you can create a cookie helper
2) the SignInMessage holds most of what you need -> Don't forget about LoginViewModel
Once your user or application is signed in, session is under your control again.

How do you get HubContext in SignalR 3?

In SignalR 2 you could do something like this (taken from my blog):
var stockTickerHub = GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>();
That allows you to get a reference to the SignalR hub from outside the hub (e.g. from a stock ticker thread).
This does not seem to be available in SignalR 3. How do you achieve the equivalent functionality in the new and upcoming SignalR?
I asked the same thing to the creator of SignalR, David Fowler on Jabbr, a forum where the creators of SignalR and the architects of ASP.NET 5 hang on from time to time, and his answer to this question was to use dependency injection.
While I haven't tried it yet with SignalR 3, I am pretty sure you can inject an instance of ConnectionManager that implements IConnectionManager in your class, and use it just like you would use GlobalHost to resolve your hub context.
Again, I have not done this with SignalR3, but I hope this will get you a little closer to finding a solution.
I put together a sample for using SignalR 2 with Autofac. (In this repo I use Autofac to inject dependencies in my hub, but also to inject instances of a ConnectionManager in other classes to get the hub context).
Hope this helps. Best of luck!
Dependency injection is indeed the way and works.
Example:
public class ChatController : Controller
{
readonly IConnectionManager _connectionManager;
public ChatController(IConnectionManager connectionManager)
{
_connectionManager = connectionManager;
}
public IActionResult Chat(string message)
{
IHubContext context = _connectionManager.GetHubContext<ChatHub>();
IConnection connection = _connectionManager.GetConnectionContext<PersistentConnection>().Connection;
context.Clients.All.NewMessage(message);
return new EmptyResult();
}
}
From every example I have seen and the few SignalR 3 apps I have implemented, you no longer have a strongly typed reference to your hub. The current methodology connects to a hub via the hub's name and URL. The On generic method creates a subscription to broadcasts from that hub and the method name you provide.
HubConnection connection = new HubConnection(ServerURL);
IHubProxy hubProxy = connection.CreateHubProxy("StockTickerHub");
hubProxy.On<StockTickerMessage>("[Your method name here]", msg => {
//your UI update logic here
});

Autofac DependencyResolutionException for ILifetimeScope for a hub in asp.net mvc, signalr, MS owin

What should be the Autofac 3.5 configuration for Asp.net Mvc 5.2, SignalR 2.1, MS Owin (Katana) 3.0? Is there less complex way to register Autofac resolvers (there is two of them now)? Or why ILifetimeScope is not visible for my hub?
The exception:
Autofac.Core.DependencyResolutionException: An exception was thrown
while invoking the constructor 'Void .ctor(Autofac.ILifetimeScope)' on
type 'OperatorHub'. --->
No scope with a Tag matching
'AutofacWebRequest' is visible from the scope in which the instance
was requested. This generally indicates that a component registered as
per-HTTP request is being requested by a SingleInstance() component
(or a similar scenario.) Under the web integration always request
dependencies from the DependencyResolver.Current or
ILifetimeScopeProvider.RequestLifetime, never from the container
itself. (See inner exception for details.) --->
Autofac.Core.DependencyResolutionException: No scope with a Tag
matching 'AutofacWebRequest' is visible from the scope in which the
instance was requested. This generally indicates that a component
registered as per-HTTP request is being requested by a
SingleInstance() component (or a similar scenario.) Under the web
integration always request dependencies from the
DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime,
never from the container itself.
In my OwinStartup (see autofac + mvc owin and autofac + signalr in owin):
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// ... registration. There is .InstancePerRequest() and .SingleInstance()
Autofac.Integration.Mvc.RegistrationExtensions.RegisterControllers(builder,typeof(MvcApplication).Assembly);
Autofac.Integration.SignalR.RegistrationExtensions.RegisterHubs(builder, Assembly.GetExecutingAssembly());
var container = builder.Build();
// 1st resolver
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
// yet the 2nd resolver!
app.MapSignalR(new HubConfiguration { Resolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container) });
}
The hub:
public class OperatorHub : Hub
{
public OperatorHub(ILifetimeScope hubLifetimeScope)
{
hubLifetimeScope.BeginLifetimeScope();
// ...
// HERE IT FALLS. The IMyService relates to MyDbContext (see below)
var myservice = hubLifetimeScope.Resolve<IMyService>();
}
}
UPDATE
The breaking component registration (EF Context:
builder.RegisterType<MyDbContext>().AsSelf().As<DbContext>().InstancePerRequest("OwinLifetimeScope");
In short the bug is the MyDbContext is not in the 'root' lifetime scope which is passed to OperatorHub constructor.
UPDATE 2
The solution with the help of #TravisIllig is to register the MyDbContext service using .InstancePerLifetimeScope() and to create the one in the hub. Another lifetime scope would be created for http request in asp mvc. Create help at Sharing Dependencies Across Apps Without Requests.
Also the hub should not dispose the given scope as it is the root one which results in ObjectDisposedException on the second run.
There is an FAQ on handling this exact exception on the Autofac doc site. The problem stems from the fact you're using InstancePerRequest in conjunction with SignalR, which, also per the documentation:
Due to SignalR internals, there is no support in SignalR for per-request lifetime dependencies.
You do appear to have looked at the Autofac SignalR docs as I see you've injected a lifetime scope to help you manage instance lifetimes, but that doesn't give you per-request lifetime scopes, it just gives you a hub lifetime scope. I might suggest revisiting that doc for a refresher.
The FAQ I mentioned, in conjunction with the SignalR integration docs, should point you to the right solution for your app. Many people simply switch their registrations from InstancePerRequest to InstancePerLifetimeScope but I strongly encourage you to read the FAQ and check out your options before just jumping to that decision. It may be the right choice, but it may not be - it depends on how your app works internally.

Using Freemarker with Restlet 2.0 in a Java EE server

I'm a bit confused with what is written in the documentation(s) for Freemarker and Restlet's freemarker extension.
Here's the situation: The restlet engine serves an HTML representation of a resource (e.g. www.mysite.com/{user}/updates). The resource returned for this URI is an HTML page containing all the updates, that is created with a freemarker template. This application is hosted on a Glassfish v3 server
Question(s):
The freemarker configuration should only be loaded once as per the freemarker documentation:
/* You should do this ONLY ONCE in the whole application life-cycle:Create and adjust the configuration */
Configuration cfg = new Configuration();
cfg.setDirectoryForTemplateLoading(
new File("/where/you/store/templates"));
cfg.setObjectWrapper(new DefaultObjectWrapper());
What is the best place to do this in a Java EE app? I am thinking of having it as context-param in web.xml and using a ServletContextListener - but I'm not sure how to go about doing that.
As per freemarker's documentation we could also add a freemarkerservlet and map .ftl url-patterns to it. But this is already mapped by a Restlet servlet (i.e., the url-pattern of "/"). So having another one for *.ftl doesn't make sense (or does it?)
So the question is basically about how best to integrate with the 'configuration' of Freemarker so that it happens only once and what is the 'entry-point' for that piece of code (who calls it). Has anyone successfully used Freemarker + restlet in a Java EE environment? Any ideas?
Thanks!
This was a tricky question - indeed. Required me to go through the implementation of the source files in org.restlet.ext.Freemarker package - Phew!
Here's how you can do it
If you need to create your OWN Configuration Object, set the 'templateLoader' to use and then have TemplateRepresentation 'work' on it for rendering:
Configuration cfg = new Configuration();
ContextTemplateLoader loader = new ContextTemplateLoader(getContext(),"war:///WEB-INF");
cfg.setTemplateLoader(loader);
TemplateRepresentation rep = null;
Mail mail = new Mail(); //The data object you wish to populate - example from Restlet itself
mail.setStatus("received");
mail.setSubject("Message to self");
mail.setContent("Doh!");
mail.setAccountRef(new Reference(getReference(), "..").getTargetRef()
.toString());
Map<String, Object> data = new HashMap<String, Object>();
data.put("status", mail.getStatus());
data.put("subject", mail.getSubject());
data.put("content", mail.getContent());
data.put("accountRef", mail.getAccountRef());
rep = new TemplateRepresentation("Mail.ftl", cfg, data, MediaType.TEXT_HTML);
return rep;
If you are happy with the default and wish to use a class loader based way of loading the templates
//Load the FreeMarker template
Representation mailFtl = new ClientResource(
LocalReference.createClapReference(getClass().getPackage())
+ "/Mail.ftl").get();
//Wraps the bean with a FreeMarker representation
return new TemplateRepresentation(mailFtl, mail, MediaType.TEXT_HTML);
If you want to initialize the Configuration Object once and set the template by calling the setServletContextForTemplateLoading(...) method on the configuration object. You could always do this in a ServletContextListener
public class Config implements ServletContextListener {
private static Configuration cfg = new Configuration();
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext sc = sce.getServletContext();
cfg.setServletContextForTemplateLoading(sc, "/WEB-INF");
}
public static Configuration getFMConfig()
{
return cfg;
}
}
And then call the static getFMConfig() and pass it to TemplateRepresentation as in 1
Things to note:
If you do get a protocol not supported Exception it'll be in case 2. Implies that the ServerResource doesn't know what protocol to use to access the file - It'll be the CLAP protocol of Restlet. You may have to set up the init-params for RestletServlet in the web.xml file and have CLAP as one of the param-values
The TemplateRepresentation has quite a few constructors - if you DON'T pass in a configuration object during instantiation (using another overloaded constructor), it will create a new Configuration() for you. So you don't have to do any configuration set up as in 2 (This may strike you as obvious but I assumed that you WOULD still need to set a configuration or it would 'pick it up from somewhere')
If you DO wish to have your OWN configuration setup you MUST pass it to one of the constructors
Have a look at the "war:///" string in the constructor of ContextTemplateLoader in 1. this is important No where is it mentioned what this baseUri reference should be, not even in the docs. I tried for quite a while before figuring it out that it should be "war:///" followed by the folder name where the templates are stored.
For case 2 you'll probably have to store the templates in the same package as the class file from where this code is accessed. If you see carefully you'll notice a LocalReference parameter as an argument to ClientResource saying that the resource is supposed to be locally present and thus you need to use the custom CLAP protocol (classLoader Access Protocol)
Personal Frustration point - why isn't all this even clarified in the documentation or ANYWHERE :)
Hope it helps someone who stumbles upon this post! Phew!

Resources