I followed Microsoft's documentation to the best I could understand it, adding this;
// Adds a default in-memory implementation of IDistributedCache.
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.CookieHttpOnly = true;
});
to ConfigureServices(IServiceCollection services) and this;
app.UseSession();
to Configure(IApplicationBuilder app, ...
Yet, when I try to access HttpContext.Session in any action of any controller
I get a null reference exception.
I'm trying to force instantiate, but can't figure out what to assign. I know HttpContext.Session is an ISession but I have no idea what implements that inteface, and it is absurd if I have to implement it myself for some key value pairs.
What am I doing wrong?
--
note:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state
As i understand you need read this one. In my case it was usefull.
UPDATED:
In this case solution was in right order of initilizing services and add their uses. Right order:
1. AddMvc
2. AddCaching
3. AddSession
4. UseSession
5. UseMvc
Related
I have the following basic SI registration in an ASP.NET WebApi project.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSimpleInjector(container, options =>
{
options
.AddAspNetCore()
.AddControllerActivation();
});
services.AddHttpContextAccessor();
services.AddScoped<Work>(services =>
{
var traceId = services.GetRequiredService<IHttpContextAccessor>().HttpContext.TraceIdentifier;
// ...
});
}
public void Configure(IApplicationBuilder app)
{
app.ApplicationServices.UseSimpleInjector(container);
// ...
container.Verify();
}
private readonly Container container = new Container();
The Problem
Container.Verify() attempts to resolve a Work instance, whose factory delegate successfully resolves an IHttpContextAccessor but its HttpContext is null because there is no current HTTP call on startup. Therefore the code dies with a null-reference exception.
I don't think there is anything we can do except guard against null but that goes against my taste in this context:
why would I do that when I know for a fact that this factory delegate should only be called during an HTTP call?
what exactly do I do if my HTTP-scoped dependency is null? Sure, return a fake BUT how do I detect that it's null for good reason and not because my web infrastructure is dying somehow?
I can't see a good solution. What do you do in this case?
I've wrote extensively about this problem in the past. Your HttpContext and its TraceIdentifier are runtime data and runtime data should be kept out of the construction of your object graphs. See for instance this article.
What the correct solution in your particular case is, is hard to say, because it depends on the details of what you are trying to construct, but the article gives some pointers.
I am using the .NET Options pattern to manage my configuration.
This configuration is needed in Controllers (easy with Dependency Injection) but also to configure other services during application startup.
I would have thought that the generic Services.Configure<MyOptionsClass> method would return an instance of MyOptionsClass but unfortunately it returns an IServiceCollection?
Is there a clean way to access a bound instance of MyOptionsClass here during startup?
var builder = WebApplication.CreateBuilder(args);
// Setup MyOptionsClass for DI
var unwantedServiceCollection = builder.Services.Configure<MyOptionsClass>(builder.Configuration.GetSection(MyOptionsClass.ConfigName));
// Already need to be able to access MyOptionsClass here:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => { options.Authority = instanceOfMyOptionsClass.Authority; });
I had a similar need in the past and this is what I did:
var configOptions = builder.Configuration.GetSection(MyOptionsClass.ConfigName);
//You can also add data annotations to your config validate it on start
builder.Services
.AddOptions<MyOptionsClass>()
.Bind(configOptions)
.ValidateDataAnnotations()
.ValidateOnStart();
var configInstance = configOptions.Get<MyOptionsClass>();
Alternatively, you can use ServiceProviderServiceExtensions GetService<> or GetRequiredService<> to get the service you need by its type. Also, please be wary of using BuildServiceProvider which may create duplicate services as mentioned here.
Hope this helps.
I can't find a way how to migrate the application from aspnet core 1.1 to 2.0 which is using cookie authentication.
Most useful resources I already know are:
https://learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/identity-2x#cookie-based-authentication
https://github.com/aspnet/announcements/issues/262
https://github.com/aspnet/Home/issues/2128 (my own question)
Unfortunately, I am still stuck. Here is how I do it:
In Startup.cs ConfigureServices(IServiceCollection services)I have:
services.AddSingleton<IConfigureNamedOptions<CookieAuthenticationOptions>, CookieAuthenticationOptionsSetup>();
and later:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
The CookieAuthenticationOptionsSetup class implements IConfigureNamedOptions and sets options.Cookie.Name = "test" inside the Configure method.
I event tried to manually create and set Name property to "Cookies".
If I try to change the name of cookie with lambda expression inside Startup.cs AddCookie method, it works as expected. But if I don't, the CookieAuthenticationOptionsSetup is never used and the default cookie name (.AspNetCore.Cookies) is used.
What I am missing?
AddCookie() by default calls AuthenticationBuilder.AddScheme. From there, we can learn how the options would be ideally registered if you passed them to the AddCookie call:
Services.Configure(authenticationScheme, configureOptions);
So let’s take a look at how Services.Configure works with named options. What ultimately gets registered on the service collection are IConfigureOptions<CookieAuthenticationOptions>>.
So I would guess that is the exact type that is being looked up later when the options are resolved via DI. And in fact, that is the exact type that is being request by the OptionsFactory that is being used by the OptionsMonitor which is requested by the authentication handler.
So, tl;dr: You are registering your custom configuration as the wrong type. You should register it as an IConfigureOptions<>:
services.AddSingleton<IConfigureOptions<CookieAuthenticationOptions>, CookieAuthenticationOptionsSetup>();
Note of course that your CookieAuthenticationOptionsSetup needs to implement IConfigureNamedOptions properly and respond to a Configure call with your scheme name (CookieAuthenticationDefaults.AuthenticationScheme).
In addition to this answer, I also had to add Name property to the class which implements IConfigureNamedOptions<T> and set it in public void Configure(string name, CookieAuthenticationOptions options) method.
Few additional notes for those who will have the same problem:
To apply Authorize attribute globally, authentication scheme must be added (worked without it previously):
services.AddMvcCore(a =>
{
var policy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build();
a.Filters.Add(new AuthorizeFilter(policy));
});
To use IsAuthenticated property inside middleware, app.UseAuthentication() must be called before registering the middleware.
It is not exactly clear what is the question...
But if you want just to set cookie name (or other options) and really do not need to implement IConfigureNamedOptions try something like this:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.Cookie.Name = "TestName";
}
I am building a support library for ASP.NET Core websites. I have a few pieces of middleware that need to be enabled, and they need to be added before any other middleware due what they do.
I can create an extension method on IWebHostBuilder to add services, likewise for configuring logging, but I don't see any way to add middleware in a programmatic way. Is there any way to do this? Looking at the source for WebHost/WebHostBuilder nothing jumped out.
Given the first comment, I may not have been clear enough. I know how to create middleware and use it. What I am trying to do is ensure that when the Configure(IApplicationBuilder app) method is called on Startup by the framework, my middleware is already in place. In a similar manner to being able to do ServiceConfiguration prior to Startup even being created. So an extension method like
public static IWebHostBuilder AddPayscaleHostingServices(this IWebHostBuilder webHostBuilder, string serviceName)
{
return webHostBuilder.ConfigureServices(collection =>
{
collection.RegisterPayscaleHostingServices();
}).ConfigureLogging(factory =>
{
});
}
gives me the ability to do some setup prior to the webHostBuilder.Build method, but I don't see anything similar for middleware/anything on IApplicationBuilder.
Thanks,
Erick
You could use a startup filter to achieve this. Startup filters allow you to configure middleware from a service resolved from the DI container.
Defining a startup filter is easy:
public class MyStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
// Configure middleware
// ...
// Call the next configure method
next(app);
};
}
}
Always make sure to call next(app) or any other middleware won't be configured.
Now register the startup filter as a singleton implementation of IStartupFilter in your ConfigureServices method:
services.AddSingleton<IStartupFilter, MyStartupFilter>();
I'm very new to ASP.NET and am attempting to pass an object between two controllers in a web application I'm making in Visual Studio 2015. The web application is using an ASP.Net 5 Preview Template Web application (if it helps, I think I'm using beta code 7 and I'm not building for DNX Core 5).
The problem I'm having is whenever I try to put anything into the TempData variable, the program seems to crash. For example, in a "Create" method I have:
[HttpPost]
public ActionResult Create(Query query)
{
switch (query.QueryTypeID)
{
case 1:
TempData["Test"] = "Test";
return RedirectToAction("Index", "EventResults");
case 2:
break;
default:
break;
}
return View();
}
In that method, I attempt to add a simple test string under the key "test". When I run the application with that TempData statement in there, I receive an error message stating
An unhandled exception occurred while processing the request.
InvalidOperationException: Session has not been configured for this application >or request.
Microsoft.AspNet.Http.Internal.DefaultHttpContext.get_Session()
I have tried going to the Web.config located in the wwwroot element of the project and adding a "sessionState" object into a "system.web" element, but this had no effect on the error.
Any help would be very much so appreciated as I've been looking for solutions for this everywhere. I'm hoping it's something stupid/blindingly obvious that I somehow missed.
In order to use middleware, such as Session, Cache, etc in ASP.NET 5, you have to enable them explicitly.
Enabling session is done by adding the appropriate nuget package in your project.json file's dependencies section (make sure that the package version matches the versions of the other dependencies you have added):
"Microsoft.AspNet.Session": "1.0.0-*"
and the appropriate session (cache) storage package as well (like the example below; in memory):
"Microsoft.Extensions.Caching.Memory": "1.0.0-*"
and adding the middleware to dependency resolution in the Startup.cs Service configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddCaching();
services.AddSession(/* options go here */);
}
and adding the middleware to OWIN in the Startup.cs OWIN configuration:
public void Configure(IApplicationBuilder app)
{
app.UseSession();
//...
Make sure that the UseSession comes before the MVC configuration.
For Asp.Net Core, make sure Asp.NetCore.Session is added.
You can configure session in StartUp.cs like below.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
// Adds a default in-memory implementation of IDistributedCache.
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.CookieHttpOnly = true;
});
}
public void Configure(IApplicationBuilder app)
{
app.UseSession();
app.UseMvcWithDefaultRoute();
}