I need to access current HttpContext in a static method or a utility service.
With classic ASP.NET MVC and System.Web, I would just use HttpContext.Current to access the context statically. But how do I do this in ASP.NET Core?
HttpContext.Current doesn't exist anymore in ASP.NET Core but there's a new IHttpContextAccessor that you can inject in your dependencies and use to retrieve the current HttpContext:
public class MyComponent : IMyComponent
{
private readonly IHttpContextAccessor _contextAccessor;
public MyComponent(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public string GetDataFromSession()
{
return _contextAccessor.HttpContext.Session.GetString(*KEY*);
}
}
Necromancing.
YES YOU CAN
Secret tip for those migrating large junks chunks (sigh, Freudian slip) of code.
The following method is an evil carbuncle of a hack which is actively engaged in carrying out the express work of satan (in the eyes of .NET Core framework developers), but it works:
In public class Startup
add a property
public IConfigurationRoot Configuration { get; }
And then add a singleton IHttpContextAccessor to DI in ConfigureServices.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
Then in Configure
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
)
{
add the DI Parameter IServiceProvider svp, so the method looks like:
public void Configure(
IApplicationBuilder app
,IHostingEnvironment env
,ILoggerFactory loggerFactory
,IServiceProvider svp)
{
Next, create a replacement class for System.Web:
namespace System.Web
{
namespace Hosting
{
public static class HostingEnvironment
{
public static bool m_IsHosted;
static HostingEnvironment()
{
m_IsHosted = false;
}
public static bool IsHosted
{
get
{
return m_IsHosted;
}
}
}
}
public static class HttpContext
{
public static IServiceProvider ServiceProvider;
static HttpContext()
{ }
public static Microsoft.AspNetCore.Http.HttpContext Current
{
get
{
// var factory2 = ServiceProvider.GetService<Microsoft.AspNetCore.Http.IHttpContextAccessor>();
object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor));
// Microsoft.AspNetCore.Http.HttpContextAccessor fac =(Microsoft.AspNetCore.Http.HttpContextAccessor)factory;
Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext;
// context.Response.WriteAsync("Test");
return context;
}
}
} // End Class HttpContext
}
Now in Configure, where you added the IServiceProvider svp, save this service provider into the static variable "ServiceProvider" in the just created dummy class System.Web.HttpContext (System.Web.HttpContext.ServiceProvider)
and set HostingEnvironment.IsHosted to true
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
this is essentially what System.Web did, just that you never saw it (I guess the variable was declared as internal instead of public).
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
ServiceProvider = svp;
System.Web.HttpContext.ServiceProvider = svp;
System.Web.Hosting.HostingEnvironment.m_IsHosted = true;
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookieMiddlewareInstance",
LoginPath = new Microsoft.AspNetCore.Http.PathString("/Account/Unauthorized/"),
AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("/Account/Forbidden/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
CookieSecure = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest
, CookieHttpOnly=false
});
Like in ASP.NET Web-Forms, you'll get a NullReference when you're trying to access a HttpContext when there is none, such as it used to be in Application_Start in global.asax.
I stress again, this only works if you actually added
services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();
like I wrote you should.
Welcome to the ServiceLocator pattern within the DI pattern ;)
For risks and side effects, ask your resident doctor or pharmacist - or study the sources of .NET Core at github.com/aspnet, and do some testing.
Perhaps a more maintainable method would be adding this helper class
namespace System.Web
{
public static class HttpContext
{
private static Microsoft.AspNetCore.Http.IHttpContextAccessor m_httpContextAccessor;
public static void Configure(Microsoft.AspNetCore.Http.IHttpContextAccessor httpContextAccessor)
{
m_httpContextAccessor = httpContextAccessor;
}
public static Microsoft.AspNetCore.Http.HttpContext Current
{
get
{
return m_httpContextAccessor.HttpContext;
}
}
}
}
And then calling HttpContext.Configure in Startup->Configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
System.Web.HttpContext.Configure(app.ApplicationServices.
GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>()
);
The most legit way I came up with was by injecting IHttpContextAccessor in your static implementation as follow:
public static class HttpHelper
{
private static IHttpContextAccessor _accessor;
public static void Configure(IHttpContextAccessor httpContextAccessor)
{
_accessor = httpContextAccessor;
}
public static HttpContext HttpContext => _accessor.HttpContext;
}
Then assigning the IHttpContextAccessor in the Startup Configure should do the job.
HttpHelper.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
I guess you should also need to register the service singleton:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Just to add to the other answers...
In ASP.NET Core 2.1, there's the AddHttpContextAccessor extension method, that will register the IHttpContextAccessor with the correct lifetime:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
// Other code...
}
}
According to this article: Accessing HttpContext outside of framework components in ASP.NET Core
namespace System.Web
{
public static class HttpContext
{
private static IHttpContextAccessor _contextAccessor;
public static Microsoft.AspNetCore.Http.HttpContext Current => _contextAccessor.HttpContext;
internal static void Configure(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
}
}
Then:
public static class StaticHttpContextExtensions
{
public static void AddHttpContextAccessor(this IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app)
{
var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
System.Web.HttpContext.Configure(httpContextAccessor);
return app;
}
}
Then:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
}
public void Configure(IApplicationBuilder app)
{
app.UseStaticHttpContext();
app.UseMvc();
}
}
You can use it like this:
using System.Web;
public class MyService
{
public void DoWork()
{
var context = HttpContext.Current;
// continue with context instance
}
}
In Startup
services.AddHttpContextAccessor();
In Controller
public class HomeController : Controller
{
private readonly IHttpContextAccessor _context;
public HomeController(IHttpContextAccessor context)
{
_context = context;
}
public IActionResult Index()
{
var context = _context.HttpContext.Request.Headers.ToList();
return View();
}
}
To access to the session object from a class without explicitly use dependency injection in class constructor follow the next steps:
Add a Singleton instance on Startup.cs (ConfigureServices):
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
In your target class declare an instance of HttpContextAccessor:
IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
Access to the session object :
string mySessionVar = _httpContextAccessor.HttpContext.Session.GetString("_MySessionVar");
EXAMPLE
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
YourClass.cs
public class YourClass {
public string yourProperty {
get{
IHttpContextAccessor _httpContextAccessor = new HttpContextAccessor();
return _httpContextAccessor.HttpContext.Session.GetString("_YourSessionVar");
}
}
}
Enjoy :)
I have a default web api template project using .NET Core 3.1 and I have registered Elastic Search NEST on my startup.cs. But when I load it, it hit error at
Singleton ImplementationType: Unable to resolve service for type 'Nest.IElasticClient' while attempting to activate in program.cs
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
and here is my startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<IProductService, ESProductService>();
services.Configure<ProductSettings>(Configuration.GetSection("product"));
services.AddElasticsearch(Configuration);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
and below is the ElasticsearchExtensions extension class
public static class ElasticsearchExtensions
{
public static void AddElasticsearch(this IServiceCollection services, IConfiguration configuration)
{
var url = configuration["elasticsearch:url"];
var defaultIndex = configuration["elasticsearch:index"];
var settings = new ConnectionSettings(new Uri(url))
.DefaultIndex(defaultIndex);
AddDefaultMappings(settings);
var client = new ElasticClient(settings);
services.AddSingleton(client);
CreateIndex(client, defaultIndex);
}
private static void AddDefaultMappings(ConnectionSettings settings)
{
settings
.DefaultMappingFor<Product>(m => m
.Ignore(p => p.Price)
.Ignore(p => p.Quantity)
.Ignore(p => p.Rating)
);
}
private static void CreateIndex(IElasticClient client, string indexName)
{
var createIndexResponse = client.Indices.Create(indexName,
index => index.Map<Product>(x => x.AutoMap())
);
}
}
Problem solved. The AddSingleton miss out the interface. services.AddSingleton<IElasticClient>(client);
I also got the same error. To fix the issue, I used IElasticClientService instead of IElasticClient. You can use below code for dependency Injection.
builder.Services.AddScoped<IElasticClientService, ElasticClientService>();
ElasticClientService has a property called "elasticClient" which can be used to do all sort of operations. Hope this helps.
i wqant ot use the autofac in my project .
i write this startup :
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly())
.AsImplementedInterfaces();
builder.Populate(services);
builder.AddDispatchers();
var conteiner = builder.Build();
return new AutofacServiceProvider(conteiner);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
nad this is my program.cs :
public class Program
{
public static void Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webHostBuilder =>
{
webHostBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>();
})
.Build();
host.Run();
}
}
but it show me this errro :
'ConfigureServices returning an System.IServiceProvider isn't supported.'
How can is solve this problem?
This is because you are trying the pre 3.0 way. Check the ConfigureServices docs. It does not supprot the IServiceProvider return type.
public virtual void ConfigureServices (Microsoft.Extensions.DependencyInjection.IServiceCollection services);
From the autofac docs:
This is not for ASP.NET Core 3+ or the .NET Core 3+ generic hosting support - ASP.NET Core 3 has deprecated the ability to return a service provider from ConfigureServices
Check Autofac net core guide post 3.0
public class Program
{
public static void Main(string[] args)
{
// ASP.NET Core 3.0+:
// The UseServiceProviderFactory call attaches the
// Autofac provider to the generic hosting mechanism.
var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webHostBuilder => {
webHostBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>();
})
.Build();
host.Run();
}
}
public class Startup
{
public Startup(IHostingEnvironment env)
{
// In ASP.NET Core 3.0 `env` will be an IWebHostingEnvironment, not IHostingEnvironment.
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
this.Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; private set; }
public ILifetimeScope AutofacContainer { get; private set; }
// ConfigureServices is where you register dependencies. This gets
// called by the runtime before the ConfigureContainer method, below.
public void ConfigureServices(IServiceCollection services)
{
// Add services to the collection. Don't build or return
// any IServiceProvider or the ConfigureContainer method
// won't get called.
services.AddOptions();
}
// ConfigureContainer is where you can register things directly
// with Autofac. This runs after ConfigureServices so the things
// here will override registrations made in ConfigureServices.
// Don't build the container; that gets done for you by the factory.
public void ConfigureContainer(ContainerBuilder builder)
{
// Register your own things directly with Autofac, like:
builder.RegisterModule(new MyApplicationModule());
}
// Configure is where you add middleware. This is called after
// ConfigureContainer. You can use IApplicationBuilder.ApplicationServices
// here if you need to resolve things from the container.
public void Configure(
IApplicationBuilder app,
ILoggerFactory loggerFactory)
{
// If, for some reason, you need a reference to the built container, you
// can use the convenience extension method GetAutofacRoot.
this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
}
}
I am getting an error when trying to call the controller below using Lamar to resolve the dependencies at runtime.
I have tried .AddControllersAsServices() and without and still get the same result.
Using
ASP.NET Core: 3.1
Lamar
Container.GetInstance<IDataAccess>() works inside the watch window but will not resolve at runtime
Container.WhatDoIHave() also shows that the dependency is there
Question?
What am I missing in Lamar configuration to resolve the controllers?
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly IDataAccess _dataAccess;
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(IDataAccess dataAccess, ILogger<WeatherForecastController> logger)
{
_dataAccess = dataAccess;
}
[HttpGet]
public IEnumerable<string> Get()
{
return _dataAccess.GetAll();
}
}
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public IContainer Container { get; private set; }
public void ConfigureContainer(ServiceRegistry services)
{
Container = new Container(cfg =>
{
cfg.Scan(scanner =>
{
scanner.AssembliesAndExecutablesFromApplicationBaseDirectory(a =>
a.FullName.Contains("Test3.1"));
scanner.WithDefaultConventions();
scanner.SingleImplementationsOfInterface();
});
});
services
.AddControllers(options =>
{
// Disable automatic fallback to JSON
options.ReturnHttpNotAcceptable = true;
// Honor browser's Accept header (e.g. Chrome)
options.RespectBrowserAcceptHeader = true;
})
.AddControllersAsServices();
services.AddMvc()
.AddControllersAsServices();
Container.WhatDidIScan();
Container.WhatDoIHave();
Console.Write("Container Instantiated");
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseLamar()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>();
});
}
An unhandled exception occurred while processing the request.
LamarException: Cannot build registered instance weatherForecastController of 'Test3._1.Controllers.WeatherForecastController':
Cannot fill the dependencies of any of the public constructors
Available constructors:new WeatherForecastController(IDataAccess dataAccess, ILogger<Test3._1.Controllers.WeatherForecastController> logger)
* IDataAccess is not registered within this container and cannot be auto discovered by any missing family policy
The error message indicates that the container can't resolve the controller's dependencies. Make sure those dependencies are registered with the container so it knows how to resolve them when activating controllers.
This is because separate containers are being configured in Startup and the one used by the framework is unaware of IDataAccess as the Scan was not applied to its container.
Reference Lamar - Integration with ASP.Net Core
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
//REMOVED IContainer. It is not needed
public void ConfigureContainer(ServiceRegistry services) {
//Apply scan to the registry used by framework so container is aware of types.
services.Scan(scanner => {
scanner.AssembliesAndExecutablesFromApplicationBaseDirectory(a =>
a.FullName.Contains("Test3.1"));
scanner.WithDefaultConventions();
scanner.SingleImplementationsOfInterface();
});
services
.AddControllers(options => {
// Disable automatic fallback to JSON
options.ReturnHttpNotAcceptable = true;
// Honor browser's Accept header (e.g. Chrome)
options.RespectBrowserAcceptHeader = true;
})
.AddControllersAsServices();
services.AddMvc()
.AddControllersAsServices();
services.WhatDidIScan();
services.WhatDoIHave();
Console.Write("Container Instantiated");
}
//...omitted for brevity
}
This is the code in my Startup.cs file and 2 of my three methods are running on build. However I added the bottom method public void PackageRequestDataAccess and for some reason its not running.
namespace Company.Shipping.Service
{
public class Startup
{
private IHostingEnvironment _environment;
private IConfigurationRoot _configurationRoot;
public Startup(IHostingEnvironment env)
{
_environment = env;
}
public void ConfigureServices(IServiceCollection services)
{
//Code Ran successfully here
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
//Code running successfully here
}
//Method below not running
public void PackageRequestDataAccess(Common.ServiceHost.WebHost.ServiceConfiguration configuration, IServiceCollection services)
{
IMongoCollection<PackageDataEntity> _reqrespcollection;
MongoDBRepository<PackageDataEntity> _repo = new MongoDBRepository<PackageDataEntity>(configuration.ConnectionStrings["MongoDB"]);
_reqrespcollection = _repo.Collection;
int _expiry = Convert.ToInt32(configuration.Settings["ShippingReqRespDataTTL"]);
TimeSpan _ttl = new TimeSpan(0, 0, _expiry);
CreateIndexOptions index = new CreateIndexOptions();
index.ExpireAfter = _ttl;
var _key = Builders<PackageDataEntity>.IndexKeys.Ascending("RequestSentOn");
_reqrespcollection.Indexes.CreateOneAsync(_key);
}
}
}
I need to run all these three methods whenever the application starts.
As per the MSDN docs available here only the Configure and ConfigureServices are called during startup.
The Startup class must include a Configure method and can optionally
include a ConfigureServices method, both of which are called when the
application starts.
In you case, may be you can add your logic to any one of this method or just call the method from the above method(s).