how to use ApplicationServices of IApplicationBuilder - asp.net-core

I have asp.net core 3.1 web api app where I have registered a service as singleton,
services.AddSingleton<ISecretKeyReader, AzureKeyVaultReader>();
Now I am using BuildServiceProvider to register Logging like this
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ISecretKeyReader, AzureKeyVaultReader>();
services.AddLogging((builder) =>
{
var provider = services.BuildServiceProvider().GetRequiredService<ISecretKeyReader>();
});
}
This above code giving warning like,
Calling BuildServiceProvider from application code result in an additional copy of singleton service being created. Consider alternative such as dependency injection as parameter to configure.
Now I am seeing we have IServiceProvider option in IApplicationBuilder,
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var x = app.ApplicationServices;
}
But not sure how to use this in ConfigureServices. Any suggestion? Thanks!

I'm not sure if this will help solve your problem, but one,
What if you use Transient instead if Singleton ...
services.AddTransient<ISecretKeyReader, AzureKeyVaultReader>();
Or, two ..
Pass IApplicationBuilder as an argument to the ConfigureServices method, so you a third parameter to that method that will be resolved using dependency injection.

Related

Asp.net core, when in debug, bypass Authorization

I have a test web site which uses the aspnetCore [AuthorizeAttribute] at the entire controller level to ensure only Authenticated Users can hit this site.
While we debug and test new features, we constantly have to comment out the attribute in each controller, which I know will be easy to forget and might get merged some day.
We've had good success with checking to see if a Debugger is attached before...I am wondering which AuthenticationScheme I should specify to allow anonymous, only if debugging.
I extend the base AuthorizeAttribute so I have an easy place to shim in some code.
public class MyAppAuthorizeAttribute : AuthorizeAttribute
{
public MyAppAuthorizeAttribute()
: base(Policies.MyAppAuthorize)
{
if (System.Diagnostics.Debugger.IsAttached)
{
Console.WriteLine("Skipping auth for debug"); //we hit this line but...
this.AuthenticationSchemes = AllowAnonymousAttribute //this setting does not work
}
else
{
this.AuthenticationSchemes = "IntegratedWindowsAuthentication";
}
}
}
Seems like a good candidate for IWebHostingEnvironment.IsDevelopment():
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (!env.IsDevelopment())
{
app.UseAuthentication();
app.UseAuthorization(); // maybe optional, depends on your case
}
...
Your requirement may be necessary in some cases where the user is required to be authenticated BUT not actually referenced in the code (e.g: the code does not access any info on the current identity, especially related to the business model of the user).
So I assume that you are aware of that because the following will just simply remove the requirement to check for authenticated user when the code runs in the development environment. There is another way to auto sign-in a dummy user which can be better in some scenarios.
Here is the first solution, it configures a default policy only which does not include the DenyAnonymousAuthorizationRequirement (which is the only requirement contained in the default policy). That means if you have multiple policies used somewhere (with AuthorizeAttribute), only the default will be ignored (while debugging). The second solution (shown later) may suit that scenario better.
//inject IHostingEnvironment in the Startup constructor
public Startup(IConfiguration configuration, IHostingEnvironment env){
HostingEnvironment = env;
}
public IHostingEnvironment HostingEnvironment { get; }
//in the ConfigureServices method in Startup.cs
services.AddAuthorization(o => {
if (HostingEnvironment.IsDevelopment()){
o.DefaultPolicy = new AuthorizationPolicyBuilder(CookieAuthenticationDefaults.AuthenticationScheme)
//there should be at least 1 requirement
//here we add a simple always-passed assertion
.RequireAssertion(e => true).Build();
}
//...
});
We need to use IHostingEnvironment (in .net core 2.2, since 3.0 we have 2 alternatives IWebHostEnvironment and IHostEnvironment) so we inject it in the Startup constructor and store it in a readonly property (as you see above). There is another way is try to get the ASPNETCORE_ENVIRONMENT variable directly like this:
var isDevelopment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development";
Here is the second solution in which you use a custom global IAsyncAuthorizationFilter to auto sign-in a dummy user (so it's always authenticated for all requests).
public class AllowAnonymousFilterAttribute : Attribute, IAsyncAuthorizationFilter
{
readonly IHostingEnvironment _hostingEnvironment;
public AllowAnonymousFilterAttribute(IHostingEnvironment env){
_hostingEnvironment = env;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (_hostingEnvironment.IsDevelopment() && !context.HttpContext.User.Identity.IsAuthenticated)
{
//prepare a dummy user to auto sign-in
HttpContext.User = new ClaimsPrincipal(new[] {
new ClaimsIdentity(new []{ new Claim(ClaimTypes.NameIdentifier,"admin")},
CookieAuthenticationDefaults.AuthenticationScheme)
});
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
HttpContext.User);
}
}
}
Register the filter globally, here I write code for .net core 2.2:
services.AddMvc(o => {
//register the filter only for development (should be debugging)
if (HostingEnvironment.IsDevelopment()){
o.Filters.Add<AllowAnonymousFilterAttribute>();
}
//...
});
I'm sure there are still some other solutions but what I've introduced here are fairly simple and good enough for your purpose.
P/S: the first solution I've introduced above suits for .net core 2.2 (actually currently I do not have access to newer versions of .net core, it's a pity). For the newer versions, the Authorization middleware is separate so you may just simply not call .UseAuthorization() middleware in the Configure method (of course for development environment only) as one other answer suggests.

How to get rid of warning "Calling 'BuildServiceProvider' from application code..."

I have the following code that shows the warning...
Calling 'BuildServiceProvider' from application code results in an
additional copy of singleton services being created. Consider
alternatives such as dependency injecting services as parameters to
'Configure'.
Here is an abbreviated snippet from ConfigureServices that shows how this is being used. Basically I need to resolve the AdminDbContext to pass it to the Initialize routine.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AdminDbContext>(o => o.UseSqlServer("config settings here"));
CustomerConnectionStrings.Instance.Initialize(services.BuildServiceProvider().GetService<AdminDbContext>());
}
What do I need to do to get rid of this warning?
I have seen other posts that show how to do this, but I am not making the connection for my specific use case.
While I believe this might be an XY problem.
I would advise following the suggestions in the warning
Consider alternatives such as dependency injecting services as parameters to 'Configure'.
//...
public void ConfigureServices(IServiceCollection services) {
services.AddDbContext<AdminDbContext>(o => o.UseSqlServer("config settings here"));
//...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, AdminDbContext adminDb) {
CustomerConnectionStrings.Instance.Initialize(adminDb);
//...
}
#Nkosi answer is a correct but in the case someone is looking for an alternative you can get any service from the app:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, AdminDbContext adminDb) {
CustomerConnectionStrings.Instance.Initialize(App.ApplicationServices.GetService<IMyService>());
//...
}
And another option for anyone who comes across this. Sometimes its better to work with singletons rather than static classes:
When you intialize CustomerConnectionStrings you will request AdminDBContext and pass it there.

Will Autofac support IWebHostBuilder API in netcore 3+?

https://autofaccn.readthedocs.io/en/latest/integration/aspnetcore.html#asp-net-core-3-0-and-generic-hosting
It looks like Autofac only supports the generic hosting API, IHostBuilder. I wonder if the old asp netcore 2.x documentation is still relevant to asp netcore 3 applications.
Also, I found https://github.com/autofac/Autofac.AspNetCore has not been updated for a long time, so I guess Autofac has no intention to support IWebHostBuilder in the future...
Do we have any guideline about how to set up Autofac in AspNetCore 3.x using the IWebHostBuilder API?
I read about [this][https://stackoverflow.com/questions/59980827/service-fabric-aspnet-core-3-1-autofac-webhostbuilder] post, and it does not answer my question.
In ASP.NET Core 3.x they changed how dependency injection integrates and, no, the version 2.x documentation no longer applies - either in the Autofac case or in the ASP.NET case. ASP.NET Core intentionally shifted to the generic hosting model, where the web host is a layer on top of that.
It's not that Autofac "has no intention of supporting IWebHostBuilder", it's that that's not an option in ASP.NET Core 3. It changed at the framework level; that's not how you integrate with ASP.NET Core anymore. You don't attach the DI factory to the web host anymore, you attach it to the outer generic host.
You do register things in your Startup class just like in ASP.NET Core 2.
The docs you linked to explain all of that and show examples. You can also see in the Microsoft ASP.NET Core 2 to 3 migration docs that HostBuilder replaces WebHostBuilder; and that WebHostBuilder, while it might still exist, is being deprecated and you shouldn't use it.
You can create constructor parameters have IWebHostEnvironment on the Startup class and asp.net core will auto inject IWebHostEnvironment.
Register IWebHostBuilder as instance on the ConfigureContainer.
See the below code.
public class Startup
{
private readonly IWebHostEnvironment _environment;
// Auto injection IWebHostEnvironment
public Startup(IWebHostEnvironment environment)
{
_environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
// ...
}
// Autofac ID
public void ConfigureContainer(ContainerBuilder builder)
{
// Register your own things directly with Autofac, like:
builder.Register<SampleClass>();
// Register your IWebHostEnvironment
builder.RegisterInstance(_environment);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
}
}
public class SampleClass
{
private readonly IWebHostEnvironment _environment;
// If you register IWebHostEnvironment on the Startup, IWebHostEnvironment will auto inject.
public SampleClass(IWebHostEnvironment environment)
{
_environment = environment;
}
}
When you resolve the SampleClass, you can see the IWebHostEnvironment is auto injected to constructor.

How create a middleware with api endpoints in .NET Core

I have created the web application with the web api. The application contains some Controllers for example TodoController:
namespace TodoApi.Controllers
{
[Route("api/[controller]")]
public class TodoController : Controller
{
private readonly TodoContext _context;
public TodoController(TodoContext context)
{
_context = context;
}
[HttpGet]
public IEnumerable<TodoItem> GetAll()
{
return _context.TodoItems.ToList();
}
}
}
If I create the GET request - /api/todo - I get the list of Todos from database.
I have a list of controllers and api endpoints like above.
I would like distribute this api to another application ideally like middleware - my idea is register in Startup.cs like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddTodoApi();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseTodoApi();
}
This will be awesome use case for my api but I don't know how this controllers api endpoints rewrite like middleware and return same JSON data same approache like using classic Controllers.
How can I write the middleware in .NET Core for creating API endpoints?
Instead of the separate middleware, you may configure the MVC middleware to discovery controllers from another assembly:
// using System.Reflection;
public void ConfigureServices(IServiceCollection services)
{
...
services
.AddMvc()
.AddApplicationPart(typeof(TodoController).GetTypeInfo().Assembly);
Controllers are part of MVC middleware, they are not a separate part of request pipeline (but this is what middlewares are). When you register the custom middleware, it by default invokes on each request and you have HttpContext context as an input parameter to work with/edit
Request/Response data. But ASP.NET Core provides Map* extensions that are used as a convention for branching the pipeline.
Map branches the request pipeline based on matches of the given request path. If the request path starts with the given path, the branch is executed.
Example:
private static void HandleMapTodo(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("/api/todo was handled");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/api/todo", HandleMapTodo);
}
Note, that as middleware knows nothing about MVC middleware, you have only access to "raw" request and do not have features like model binding or MVC action filters.
Because it looks like the perfect microservices approach (similar than what my team is doing right now) I'd create a client assembly that can consume your API, the one that contains your TodoController, if you define a contract, and interface, for that API you can register it in your other assembly as it was a midleware and also you could mock that behaviour in your unit tests.
So, as I said, you could inject your client in ConfigureServices method, you can create:
public static IServiceCollection AddTodoRestClient(this IServiceCollection services)
{
services.AddSingleton<ITodoRestClient, TodoRestClient>();
return services;
}
Also consider that you will need to provide the enpoint so, it might looks like:
public static IServiceCollection AddConfiguredTodoClient(this IServiceCollection services, string todoEndpoint)
{
AddTodoClient(services);
ITodoRestClient todoRestClient = services.BuildServiceProvider().GetService<ITodoRestClient>();
// Imagine you have a configure method...
todoRestClient.Configure(services, todoEndpoint);
return services;
}
You can create those methods in a TodoRestClientInjector class and use them in Configure method on your startup.
I hope it helps
--- MORE DETAILS TO ANSWER COMMENTS ---
For me TodoClient is a Rest client library that implements calls to the ToDo API, (I've edited previous code to be TodoRestClient) methos like, i.e., CreateTodoItem(TodoDto todoItem) which implementation would call to the TodoController.Post([FromBody] item) or GetTodos() which wuold call TodoController.Get() and so on and so forth....
Regarding the enpoints... This approach implies to have (at least) two different applications (.NET Core apps), on the one hand the ASP NET Core app that has your TodoController and on the other hand a console application or another ASP NET Core API on which startup class you'll do the inyection adn the Rest client (the Todo Rest client) configuration ...
In a microservices approach using docker, in a dev environment, you'll use docker-compose-yml, but in a traditional approach you'll use concrete ports to define the endpoints...
So, imagine that you have in the second service a controller that need to use TodoController, to achieve so I'll use the above aproach and the "SecondController" would look like:
public class SecondController : Controller
{
private readonly SecondContext _context;
private readonly TodoRestClient _todoRestClient;
public TodoController(SecondContext context, ITodoRestClient todoRestClient)
{
_context = context;
_todoRestClient= todoRestClient;
}
// Whatever logic in this second controller... but the usage would be like:
_todoRestClient.GetTodos()
}
Just few final hints: it's key to minimize calls between services because it increases latency, and more and more if this happens on cascade. Also consider Docker usage, looks challenging but it is quite easy to start and, indeed, is thought to be used in scenarios that the one you presented and solutions like mine.
Again, I hope it helps.
Juan

ASP.NET Core: Can not resolve a service instance through CallContextServiceLocator.Locator.ServiceProvider

This is part of my ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
...
//bus
services.AddSingleton<IRouteMessages, MessageRouter>();
services.AddSingleton<IBus, DirectBus>();
////
...
}
I'm trying to resolve the instance of IRouteMessages interface in my RegisterCommandHandlersInMessageRouter class:
public class RegisterCommandHandlersInMessageRouter
{
...
public static void BootStrap()
{
var router = CallContextServiceLocator.Locator.ServiceProvider.GetService(typeof (IRouteMessages));
new RegisterCommandHandlersInMessageRouter().RegisterRoutes(router as MessageRouter);
}
...
}
router variable is always null. Yet in my controllers where IRouterMessages is resolved automatically (in constructors) everything is fine.
I'm not sure what other parts of my code could be useful. I will provide more details.
Don't EVER use CallContextServiceLocator, this completely beats the purpose of having dependency injection. And NEVER relay on it.
CallContextServiceLocator is only used in some of the internal ASP.NET Core and is never be supposed to be used by developers creating ASP.NET Core applications. That being said, it can be removed, made internal or inaccessible at any time which would break existing applications.
Additionally, the CallContextServiceLocator only had runtime services registered (DNX Services, deprecated anyways). Source: David Fowl from ASP.NET Core team.
Infact CallContextServiceLocator is being removed in RC2, see the announcement.
Removed support for CallContextServiceLocator. Use PlatformServices and CompilationServices instead.
Instead, only use the built-in dependency injection, like this:
public static class RegisterCommandHandlersInMessageRouter
{
...
// This is extension method now
public static void RegisterCommandHandlers(this IServiceProvider services)
{
var router = services.GetService(typeof (IRouteMessages));
new RegisterCommandHandlersInMessageRouter().RegisterRoutes(router as MessageRouter);
}
...
}
and call it in your Startup.cs
public void Configure(IServiceProvider services)
{
...
services.RegisterCommandHandlers();
...
}