Bakground:
I want to develop a multi-tenant application in ASP.NET Core and have been looking into Ben Fosters Saaskit library which seems to provide good solutions for common problems in multitenancy applications.
Problem:
The SaasKit have a UsePerTenant method which is nice for doing different things per-request depending on current tenant.
My goal is to use the UsePerTenant method combined with different IOptions objects injected via dependency injection. This can be used in the authentication middleware like
AddAuthentication().AddCookie(..).AddOpenIdConnect(...)
Which is configured in the ConfigureServices method in Startup.cs
public class Startup
{
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
...
}
// Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
...
}
}
I can’t make the authentication middleware in ASP.NET 2.0+ use different IOptions objects per-request since the ConfigureServices method in the Startup.cs file only runs once every application startup and the UsePerTenant method should be used in the Configure method which is running for each incoming/outgoing request in the ASP.NET pipeline.
Question:
How to dynamically change cookie and OpenID Connect options in the ConfigureServices method based on current tenant?
I have found a good way to get per tenant options for any type of ASP.NET Core options, including cookie or openID Connect. I have wrapped this up into a framework called Finbuckle.MultiTenant.
It basically boils down to a setup that looks like this:
services.AddMultiTenant().
WithInMemoryStore()).
WithRouteStrategy().
WithPerTenantOptionsConfig<CookieAuthenticationOptions>((o, tenantContext) => o.Cookie.Name += tenantContext.Id);
See my here for more information if you are curious: https://www.finbuckle.com/MultiTenant
The following PR provides a solution for the above question.
https://github.com/saaskit/saaskit/pull/96
The PR have been merged with the "master" branch now.
It wasn't merged yet (November 2018)
Related
I am reading the Autofac documentation related to its integration with OWIN, and I get confused by the example they put on their website:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// STANDARD WEB API SETUP:
// Get your HttpConfiguration. In OWIN, you'll create one
// rather than using GlobalConfiguration.
var config = new HttpConfiguration();
// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// Run other optional steps, like registering filters,
// per-controller-type services, etc., then set the dependency resolver
// to be Autofac.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// OWIN WEB API SETUP:
// Register the Autofac middleware FIRST, then the Autofac Web API middleware,
// and finally the standard Web API middleware.
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
}
Where I am stuck now is on line config.DependencyResolver = new AutofacWebApiDependencyResolver(container); the webapi dependency resolver is set to autofac, and what is the reason that app.UseAutofacWebApi(config); must be called?
The extension method is to "Extends the Autofac lifetime scope added from the OWIN pipeline through to the Web API dependency scope", but the autofac lifetime scope is shared by setting dependency resolver, is this call still necessary?
It would also be very helpful if you can provide me the use cases of UseAutofacWebApi. Thanks!
The short answer is, yes, it's needed.
Usually a way to test this sort of thing is to see if you can get everything you need without it. I mean, if your app works and things are injected right, that's enough, right?
The longer answer involves you needing to understand that Web API is not natively part of OWIN. OWIN is kind of a bolt-on. The way Web API integrates is, very basically, that the Web API pipeline is jammed into the OWIN pipeline as a middleware step. You can do other stuff with OWIN, too, like adding your own middleware and so on. It's a little beyond the scope of the question here to go through all the details of the ASP.NET pipeline, OWIN, how the middleware interacts, etc. A quick Google search for how does web api work with owin brings up a ton of documentation, blog articles, and explanations.
The important aspect there, though, is that there are basically two parts - the OWIN part, with its pipeline, and the Web API part, with its "sub pipeline."
Since the OWIN pipeline starts before the Web API pipeline, in order to make the two work together and have a request lifetime for the whole of the OWIN pipeline, you have to use the Autofac middleware to do that initialization. Then that same scope from the OWIN pipeline needs to make it into Web API. That also means when the Web API request is done, the OWIN pipeline needs to be able to handle the disposal of things rather than letting Web API handle it.
Hence the documentation: "Extends the Autofac lifetime scope added from the OWIN pipeline through to the Web API dependency scope." It's taking the scope created at the start of the OWIN pipeline and making sure it's the same scope that goes into the Web API pipeline.
Some applications not only have Web API but also ASP.NET MVC, all of which is trying to be coordinated through that OWIN pipeline and needing everything to work together. All of the stuff Autofac has in place to get Web API, MVC, and OWIN wired up is there for a reason - to make sure it all plays nicely together.
Generally if something is optional, the docs will explicitly say so. For example in the Autofac Web API documentation it's noted that it's optional to register filter and model binder provider handling. In this case, it's not really optional.
I'm upgrading an ASP.NET Core application from Framework 2.2 to 3.1. It also uses Entity Framework Core.
In the Startup.ConfigureServices method, there is this code:
services.AddEntityFrameworkNpgsql()
.AddDbContext<MainDbContext>(options => options
.UseNpgsql(Configuration.GetConnectionString("MainDbContext")));
Everything was fine with .NET Core 2.2. With .NET Core 3.1, I get this warning on every application start:
'AddEntityFramework*' was called on the service provider, but 'UseInternalServiceProvider' wasn't called in the DbContext options configuration. Remove the 'AddEntityFramework*' call as in most cases it's not needed and might cause conflicts with other products and services registered in the same service provider.
Looking up the UseInternalServiceProvider method, it looks like that should be called on the options to pass on the main service provider. Unfortunately, at this point, the service provider does not exist yet. It is just about to be built.
I don't understand what the problem is and what this warning wants to tell me, but failed to do. How can I make that warning go away? The web doesn't know about this message yet.
Remove AddEntityFrameworkNpgsql. The docs explain that :
Calling this method is no longer necessary when building most applications, including those that use dependency injection in ASP.NET or elsewhere. It is only needed when building the internal service provider for use with the method. This is not recommend other than for some advanced scenarios.
The actual Getting Started page For Npgsql shows there's no need for anything extra :
simply place the following in your ConfigureServices method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// Other DI initializations
services.AddDbContext<BloggingContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("BloggingContext")));
}
I have an existing ASP.NET application and I want it to act as an SAML2 SP using SustainSys SAML2. The documentation says that I should use the web.config file but it gets ignored on .NET Core.
So, how do I start?
I assume I should write a bit of code to my Startup class, but what and where? The thread linked above tells some things but I need more details. How do I set Saml2 as the default challenge protocol for authentication?
I added the code from the documentation (services.AddAuthentication().AddSaml2(...); in void ConfigureServices() and even app.UseAuthorization(); in void Configure()) and when I try to add the [Authorize] attribute to a controller, I get an exception telling me "a middleware was not found that supports authorization. Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code."
Use the Sustainsys.Saml2.AspNetCore2 package and add it in startup.
Web.config is not used on AspNet Core, you have to configure in startup.
I have the following registration in asp.net core startup.cs file:
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<UserService>().As<IUserService>();
}
This is to configure the Autofac container. And I have another integration test project where I have a CustomWebApplicationFactory class and I'm trying to replace the implementation of IUserService interface.
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.AddSingleton<IUserService, TestUsersService>();
});
}
It seems not work when I debug the test project and the implementation of IUserService is still UserService.
I tried registering UserService directly in Startup.ConfigureServices method with ASP.NET Core built-in IServiceCollection and it worked when debugging:
services.AddSingleton<IUserService, UserService>();
So, how can I fix the problem when I use Autofac as the IoC container and the integration test project will work properly as I expect?
You are likely running into an order of operations problem. In general, last in wins. That goes for Autofac and for the base Microsoft DI container.
Assuming you've read through the docs on Autofac ASP.NET Core integration you'll see that when ConfigureContainer is in place the order of operations is roughly:
WebHost specific ConfigureServices
Startup class ConfigureServices
Startup class ConfigureContainer
When adding ConfigureTestServices in place, it looks like (though I haven't stepped through) it runs after the WebHost and Startup class ConfigureServices... but it's still running before ConfigureContainer.
This would be easy enough to test - create a service interface with three different implementations. Register a different implementation at each level. Resolve the interface in a controller. Which one did you get? That was the last one to run. Now remove that registration from the app and try again. What's the next one you get? That's the second to the last one. And so on.
Autofac takes a pre-built IServicesCollection and cycles through it, adding that to the native Autofac container. Once that's happened, it doesn't matter if you modify the collection. Autofac has no control over the order of the execution of the startup mechanism in ASP.NET Core; it just knows that ASP.NET Core says, "Here's the final service collection to go ahead and import!" If that's not happening at the right stage, you'll have to do one of two things:
Move the registration you need to override out of ConfigureContainer and into one of the ConfigureServices methods, using the Microsoft registration language instead of native Autofac.
Perform the override some other way like using an ASPNETCORE_ENVIRONMENT setting of Test and providing a ConfigureTestContainer method. (Examples of environment specific registration methods are in the docs.)
When using ContainerBuilder like this:
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<UserService>().As<IUserService>();
}
You must use ConfigureTestContainer rather than ConfigureTestServices as follows:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestContainer<ContainerBuilder>(containerBuilder =>
{
containerBuilder.RegisterType<TestUsersService>().As<IUserService>();
});
}
This is executed after the call to ConfigureContainer and will correctly override IUserService with TestUsersService
For the ones coming from google I'd like to add to Michael's excellent answer that ConfigureTestContainer does not work with the generic host recommended over the web host by Microsoft as of .NET Core 3.0. There is however a workaround proposed by Alistair Evans from the Autofac team. Unfortunately, it relies on the deprecated IStartupConfigureContainerFilter that will probably be removed in .NET 5.0.
This means that currently in .NET 5.0 there is no way to mock dependencies injected by an external DI container in integration tests when using the generic host.
Luckily, David Fowler from the ASP.NET team is looking into the issue.
well, I am now learning aspnet core , I can't understand when does the application start its server(like IIS or KestrelServer),and how the server listen the httprequest and forwards the request to the application. can anybody help me? thanks
Well, let's start from beginning (As I couldn't figure out your knowledge about C#)
Every C# application must contain a single Main method specifying where program execution is to begin, so there it is, by default the templates have a Class Program where you can set the type of WebServer that you'll use, and tell the server to start listening for HTTP requests, something like:
public static void Main(string[] args)
{
var config = new ConfigurationBuilder()
.AddCommandLine(args)
.Build();
var host = new WebHostBuilder()
.UseKestrel()
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
host.Run();
}
In AspNetCore and even in AspNet (MVC or WebApi) you can (and should) use OWIN (aka Katana or vNext, that are Microsoft's OWIN implementations for AspNet and AspNetCore respectively).
OWIN represents a Interface (just a specification), that tell how WebServers should comunicate with WebApplications. Normally it handle the Http Requests to a pipeline that you can plug Middlewares, like Authentication/Authorization, Log, Error Handlings and so on, and in the end of the pipeline you should plug your Web Application.
In AspNetCore you set your Middleware Pipeline by using UseStartup<MyStartupClass> in your Host configuration see Main method above and simple as that your Pipeline will handle every HttpRequest.
When building a MVC applications in AspNetCore (.UseMvc()) you are setting a middleware that tells your application to look for classes that inherit from Microsoft.AspNetCore.Mvc.Controller to look for RESTful entry points (HTTP GET, POST...)
This is just a simple overview, and you can learn a lot more looking in the documentation of this technologies. Just search for tags like Katana, vNext, OWIN, OWIN Middleware, OWIN Pipeline...
ASP.NET Core application anatomy is discussed here at this asp.net core introduction.
Some important text that answers your question is as follows from the tutorial:
An ASP.NET Core app is simply a console app that creates a web server in its Main method. Main uses WebHostBuilder, which follows the builder pattern, to create a web application host. The builder has methods that define the web server (for example UseKestrel) and the startup class (UseStartup). In the example above, the Kestrel web server is used, but other web servers can be specified. The Startup class is where you define the request handling pipeline and where any services needed by the app are configured. The Startup class must be public and contain the following methods:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app)
{
}
}
I guess this will help you to understand how asp.net core handles Http requests.
Thanks