Simplify HttpContextAccessor (now singleton)
This commit is contained in:
parent
f786fb7bd0
commit
16a811479e
|
|
@ -16,17 +16,20 @@ namespace Microsoft.AspNet.Hosting
|
||||||
private readonly IStartupManager _startupManager;
|
private readonly IStartupManager _startupManager;
|
||||||
private readonly IApplicationBuilderFactory _builderFactory;
|
private readonly IApplicationBuilderFactory _builderFactory;
|
||||||
private readonly IHttpContextFactory _httpContextFactory;
|
private readonly IHttpContextFactory _httpContextFactory;
|
||||||
|
private readonly IHttpContextAccessor _contextAccessor;
|
||||||
|
|
||||||
public HostingEngine(
|
public HostingEngine(
|
||||||
IServerManager serverManager,
|
IServerManager serverManager,
|
||||||
IStartupManager startupManager,
|
IStartupManager startupManager,
|
||||||
IApplicationBuilderFactory builderFactory,
|
IApplicationBuilderFactory builderFactory,
|
||||||
IHttpContextFactory httpContextFactory)
|
IHttpContextFactory httpContextFactory,
|
||||||
|
IHttpContextAccessor contextAccessor)
|
||||||
{
|
{
|
||||||
_serverManager = serverManager;
|
_serverManager = serverManager;
|
||||||
_startupManager = startupManager;
|
_startupManager = startupManager;
|
||||||
_builderFactory = builderFactory;
|
_builderFactory = builderFactory;
|
||||||
_httpContextFactory = httpContextFactory;
|
_httpContextFactory = httpContextFactory;
|
||||||
|
_contextAccessor = contextAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDisposable Start(HostingContext context)
|
public IDisposable Start(HostingContext context)
|
||||||
|
|
@ -37,7 +40,7 @@ namespace Microsoft.AspNet.Hosting
|
||||||
EnsureApplicationDelegate(context);
|
EnsureApplicationDelegate(context);
|
||||||
|
|
||||||
var applicationLifetime = (ApplicationLifetime)context.Services.GetRequiredService<IApplicationLifetime>();
|
var applicationLifetime = (ApplicationLifetime)context.Services.GetRequiredService<IApplicationLifetime>();
|
||||||
var pipeline = new PipelineInstance(_httpContextFactory, context.ApplicationDelegate);
|
var pipeline = new PipelineInstance(_httpContextFactory, context.ApplicationDelegate, _contextAccessor);
|
||||||
var server = context.ServerFactory.Start(context.Server, pipeline.Invoke);
|
var server = context.ServerFactory.Start(context.Server, pipeline.Invoke);
|
||||||
|
|
||||||
return new Disposable(() =>
|
return new Disposable(() =>
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.Framework.DependencyInjection
|
||||||
// TODO: Do we expect this to be provide by the runtime eventually?
|
// TODO: Do we expect this to be provide by the runtime eventually?
|
||||||
services.AddLogging(configuration);
|
services.AddLogging(configuration);
|
||||||
services.TryAdd(describer.Singleton<IHostingEnvironment, HostingEnvironment>());
|
services.TryAdd(describer.Singleton<IHostingEnvironment, HostingEnvironment>());
|
||||||
services.TryAdd(describer.Scoped<IHttpContextAccessor, HttpContextAccessor>());
|
services.TryAdd(describer.Singleton<IHttpContextAccessor, HttpContextAccessor>());
|
||||||
|
|
||||||
// REVIEW: don't try add because we pull out IEnumerable<IConfigureHostingEnvironment>?
|
// REVIEW: don't try add because we pull out IEnumerable<IConfigureHostingEnvironment>?
|
||||||
services.AddInstance<IConfigureHostingEnvironment>(new ConfigureHostingEnvironment(configuration));
|
services.AddInstance<IConfigureHostingEnvironment>(new ConfigureHostingEnvironment(configuration));
|
||||||
|
|
|
||||||
|
|
@ -14,60 +14,35 @@ namespace Microsoft.AspNet.Hosting
|
||||||
{
|
{
|
||||||
public class HttpContextAccessor : IHttpContextAccessor
|
public class HttpContextAccessor : IHttpContextAccessor
|
||||||
{
|
{
|
||||||
private HttpContext _value;
|
#if ASPNET50
|
||||||
|
private const string LogicalDataKey = "__HttpContext_Current__";
|
||||||
public bool IsRootContext { get; set; }
|
|
||||||
|
|
||||||
public HttpContext Value
|
public HttpContext Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return IsRootContext ? AccessRootHttpContext() : _value;
|
var handle = CallContext.LogicalGetData(LogicalDataKey) as ObjectHandle;
|
||||||
|
return handle != null ? handle.Unwrap() as HttpContext : null;
|
||||||
}
|
}
|
||||||
}
|
set
|
||||||
|
|
||||||
public HttpContext SetValue(HttpContext value)
|
|
||||||
{
|
|
||||||
if (IsRootContext)
|
|
||||||
{
|
{
|
||||||
return ExchangeRootHttpContext(value);
|
CallContext.LogicalSetData(LogicalDataKey, new ObjectHandle(value));
|
||||||
}
|
}
|
||||||
var prior = _value;
|
|
||||||
_value = value;
|
|
||||||
return prior;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ASPNET50
|
|
||||||
private const string LogicalDataKey = "__HttpContext_Current__";
|
|
||||||
#elif ASPNETCORE50
|
#elif ASPNETCORE50
|
||||||
private static AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();
|
private AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();
|
||||||
#endif
|
public HttpContext Value
|
||||||
|
|
||||||
private static HttpContext AccessRootHttpContext()
|
|
||||||
{
|
{
|
||||||
#if ASPNET50
|
get
|
||||||
var handle = CallContext.LogicalGetData(LogicalDataKey) as ObjectHandle;
|
{
|
||||||
return handle != null ? handle.Unwrap() as HttpContext : null;
|
return _httpContextCurrent.Value;
|
||||||
#elif ASPNETCORE50
|
}
|
||||||
return _httpContextCurrent.Value;
|
set
|
||||||
#else
|
{
|
||||||
throw new Exception("TODO: CallContext not available");
|
_httpContextCurrent.Value = value;
|
||||||
#endif
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpContext ExchangeRootHttpContext(HttpContext httpContext)
|
|
||||||
{
|
|
||||||
#if ASPNET50
|
|
||||||
var prior = CallContext.LogicalGetData(LogicalDataKey) as ObjectHandle;
|
|
||||||
CallContext.LogicalSetData(LogicalDataKey, new ObjectHandle(httpContext));
|
|
||||||
return prior != null ? prior.Unwrap() as HttpContext : null;
|
|
||||||
#elif ASPNETCORE50
|
|
||||||
var prior = _httpContextCurrent.Value;
|
|
||||||
_httpContextCurrent.Value = httpContext;
|
|
||||||
return prior;
|
|
||||||
#else
|
|
||||||
return null;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,6 @@ namespace Microsoft.AspNet.Hosting
|
||||||
{
|
{
|
||||||
public interface IHttpContextAccessor
|
public interface IHttpContextAccessor
|
||||||
{
|
{
|
||||||
bool IsRootContext { get; set; }
|
HttpContext Value { get; set; }
|
||||||
HttpContext Value { get; }
|
|
||||||
|
|
||||||
HttpContext SetValue(HttpContext value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,9 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Hosting.Builder;
|
using Microsoft.AspNet.Hosting.Builder;
|
||||||
using Microsoft.AspNet.Hosting.Server;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Hosting
|
namespace Microsoft.AspNet.Hosting
|
||||||
{
|
{
|
||||||
|
|
@ -14,16 +12,19 @@ namespace Microsoft.AspNet.Hosting
|
||||||
{
|
{
|
||||||
private readonly IHttpContextFactory _httpContextFactory;
|
private readonly IHttpContextFactory _httpContextFactory;
|
||||||
private readonly RequestDelegate _requestDelegate;
|
private readonly RequestDelegate _requestDelegate;
|
||||||
|
private readonly IHttpContextAccessor _contextAccessor;
|
||||||
|
|
||||||
public PipelineInstance(IHttpContextFactory httpContextFactory, RequestDelegate requestDelegate)
|
public PipelineInstance(IHttpContextFactory httpContextFactory, RequestDelegate requestDelegate, IHttpContextAccessor contextAccessor)
|
||||||
{
|
{
|
||||||
_httpContextFactory = httpContextFactory;
|
_httpContextFactory = httpContextFactory;
|
||||||
_requestDelegate = requestDelegate;
|
_requestDelegate = requestDelegate;
|
||||||
|
_contextAccessor = contextAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Invoke(object serverEnvironment)
|
public Task Invoke(object serverEnvironment)
|
||||||
{
|
{
|
||||||
var httpContext = _httpContextFactory.CreateHttpContext(serverEnvironment);
|
var httpContext = _httpContextFactory.CreateHttpContext(serverEnvironment);
|
||||||
|
_contextAccessor.Value = httpContext;
|
||||||
return _requestDelegate(httpContext);
|
return _requestDelegate(httpContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNet.Hosting;
|
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
|
@ -13,7 +12,6 @@ namespace Microsoft.AspNet.RequestContainer
|
||||||
public RequestServicesContainer(
|
public RequestServicesContainer(
|
||||||
HttpContext context,
|
HttpContext context,
|
||||||
IServiceScopeFactory scopeFactory,
|
IServiceScopeFactory scopeFactory,
|
||||||
IHttpContextAccessor appContextAccessor,
|
|
||||||
IServiceProvider appServiceProvider)
|
IServiceProvider appServiceProvider)
|
||||||
{
|
{
|
||||||
if (scopeFactory == null)
|
if (scopeFactory == null)
|
||||||
|
|
@ -24,12 +22,6 @@ namespace Microsoft.AspNet.RequestContainer
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
if (appContextAccessor == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(appContextAccessor));
|
|
||||||
}
|
|
||||||
|
|
||||||
AppContextAccessor = appContextAccessor;
|
|
||||||
|
|
||||||
Context = context;
|
Context = context;
|
||||||
PriorAppServices = context.ApplicationServices;
|
PriorAppServices = context.ApplicationServices;
|
||||||
|
|
@ -37,23 +29,15 @@ namespace Microsoft.AspNet.RequestContainer
|
||||||
|
|
||||||
// Begin the scope
|
// Begin the scope
|
||||||
Scope = scopeFactory.CreateScope();
|
Scope = scopeFactory.CreateScope();
|
||||||
ScopeContextAccessor = Scope.ServiceProvider.GetRequiredService<IHttpContextAccessor>();
|
|
||||||
|
|
||||||
Context.ApplicationServices = appServiceProvider;
|
Context.ApplicationServices = appServiceProvider;
|
||||||
Context.RequestServices = Scope.ServiceProvider;
|
Context.RequestServices = Scope.ServiceProvider;
|
||||||
|
|
||||||
PriorAppHttpContext = AppContextAccessor.SetValue(context);
|
|
||||||
PriorScopeHttpContext = ScopeContextAccessor.SetValue(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpContext Context { get; set; }
|
private HttpContext Context { get; set; }
|
||||||
private IServiceProvider PriorAppServices { get; set; }
|
private IServiceProvider PriorAppServices { get; set; }
|
||||||
private IServiceProvider PriorRequestServices { get; set; }
|
private IServiceProvider PriorRequestServices { get; set; }
|
||||||
private HttpContext PriorAppHttpContext { get; set; }
|
|
||||||
private HttpContext PriorScopeHttpContext { get; set; }
|
|
||||||
private IServiceScope Scope { get; set; }
|
private IServiceScope Scope { get; set; }
|
||||||
private IHttpContextAccessor ScopeContextAccessor { get; set; }
|
|
||||||
private IHttpContextAccessor AppContextAccessor { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
// CONSIDER: this could be an extension method on HttpContext instead
|
// CONSIDER: this could be an extension method on HttpContext instead
|
||||||
|
|
@ -73,29 +57,24 @@ namespace Microsoft.AspNet.RequestContainer
|
||||||
|
|
||||||
// Matches constructor of RequestContainer
|
// Matches constructor of RequestContainer
|
||||||
var rootServiceProvider = serviceProvider.GetRequiredService<IServiceProvider>();
|
var rootServiceProvider = serviceProvider.GetRequiredService<IServiceProvider>();
|
||||||
var rootHttpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
|
|
||||||
var rootServiceScopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
|
var rootServiceScopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
|
||||||
|
|
||||||
rootHttpContextAccessor.IsRootContext = true;
|
|
||||||
|
|
||||||
// Pre Scope setup
|
// Pre Scope setup
|
||||||
var priorApplicationServices = serviceProvider;
|
var priorApplicationServices = serviceProvider;
|
||||||
var priorRequestServices = serviceProvider;
|
var priorRequestServices = serviceProvider;
|
||||||
|
|
||||||
var appServiceProvider = rootServiceProvider;
|
var appServiceProvider = rootServiceProvider;
|
||||||
var appServiceScopeFactory = rootServiceScopeFactory;
|
var appServiceScopeFactory = rootServiceScopeFactory;
|
||||||
var appHttpContextAccessor = rootHttpContextAccessor;
|
|
||||||
|
|
||||||
if (priorApplicationServices != null &&
|
if (priorApplicationServices != null &&
|
||||||
priorApplicationServices != appServiceProvider)
|
priorApplicationServices != appServiceProvider)
|
||||||
{
|
{
|
||||||
appServiceProvider = priorApplicationServices;
|
appServiceProvider = priorApplicationServices;
|
||||||
appServiceScopeFactory = priorApplicationServices.GetRequiredService<IServiceScopeFactory>();
|
appServiceScopeFactory = priorApplicationServices.GetRequiredService<IServiceScopeFactory>();
|
||||||
appHttpContextAccessor = priorApplicationServices.GetRequiredService<IHttpContextAccessor>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates the scope and does the service swaps
|
// Creates the scope and does the service swaps
|
||||||
return new RequestServicesContainer(httpContext, appServiceScopeFactory, appHttpContextAccessor, appServiceProvider);
|
return new RequestServicesContainer(httpContext, appServiceScopeFactory, appServiceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable Support
|
#region IDisposable Support
|
||||||
|
|
@ -107,9 +86,6 @@ namespace Microsoft.AspNet.RequestContainer
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
ScopeContextAccessor.SetValue(PriorScopeHttpContext);
|
|
||||||
AppContextAccessor.SetValue(PriorAppHttpContext);
|
|
||||||
|
|
||||||
Context.RequestServices = PriorRequestServices;
|
Context.RequestServices = PriorRequestServices;
|
||||||
Context.ApplicationServices = PriorAppServices;
|
Context.ApplicationServices = PriorAppServices;
|
||||||
}
|
}
|
||||||
|
|
@ -123,10 +99,6 @@ namespace Microsoft.AspNet.RequestContainer
|
||||||
Context = null;
|
Context = null;
|
||||||
PriorAppServices = null;
|
PriorAppServices = null;
|
||||||
PriorRequestServices = null;
|
PriorRequestServices = null;
|
||||||
ScopeContextAccessor = null;
|
|
||||||
AppContextAccessor = null;
|
|
||||||
PriorAppHttpContext = null;
|
|
||||||
PriorScopeHttpContext = null;
|
|
||||||
|
|
||||||
disposedValue = true;
|
disposedValue = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,23 @@ namespace Microsoft.AspNet.TestHost
|
||||||
Assert.Throws<InvalidOperationException>(() => TestServer.Create(services, new Startup().Configuration));
|
Assert.Throws<InvalidOperationException>(() => TestServer.Create(services, new Startup().Configuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CanAccessHttpContext()
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection().BuildServiceProvider();
|
||||||
|
TestServer server = TestServer.Create(app =>
|
||||||
|
{
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
var accessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
|
||||||
|
return context.Response.WriteAsync("HasContext:"+(accessor.Value != null));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
string result = await server.CreateClient().GetStringAsync("/path");
|
||||||
|
Assert.Equal("HasContext:True", result);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateInvokesApp()
|
public async Task CreateInvokesApp()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue