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 IApplicationBuilderFactory _builderFactory;
|
||||
private readonly IHttpContextFactory _httpContextFactory;
|
||||
private readonly IHttpContextAccessor _contextAccessor;
|
||||
|
||||
public HostingEngine(
|
||||
IServerManager serverManager,
|
||||
IStartupManager startupManager,
|
||||
IApplicationBuilderFactory builderFactory,
|
||||
IHttpContextFactory httpContextFactory)
|
||||
IHttpContextFactory httpContextFactory,
|
||||
IHttpContextAccessor contextAccessor)
|
||||
{
|
||||
_serverManager = serverManager;
|
||||
_startupManager = startupManager;
|
||||
_builderFactory = builderFactory;
|
||||
_httpContextFactory = httpContextFactory;
|
||||
_contextAccessor = contextAccessor;
|
||||
}
|
||||
|
||||
public IDisposable Start(HostingContext context)
|
||||
|
|
@ -37,7 +40,7 @@ namespace Microsoft.AspNet.Hosting
|
|||
EnsureApplicationDelegate(context);
|
||||
|
||||
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);
|
||||
|
||||
return new Disposable(() =>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.Framework.DependencyInjection
|
|||
// TODO: Do we expect this to be provide by the runtime eventually?
|
||||
services.AddLogging(configuration);
|
||||
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>?
|
||||
services.AddInstance<IConfigureHostingEnvironment>(new ConfigureHostingEnvironment(configuration));
|
||||
|
|
|
|||
|
|
@ -14,60 +14,35 @@ namespace Microsoft.AspNet.Hosting
|
|||
{
|
||||
public class HttpContextAccessor : IHttpContextAccessor
|
||||
{
|
||||
private HttpContext _value;
|
||||
|
||||
public bool IsRootContext { get; set; }
|
||||
#if ASPNET50
|
||||
private const string LogicalDataKey = "__HttpContext_Current__";
|
||||
|
||||
public HttpContext Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return IsRootContext ? AccessRootHttpContext() : _value;
|
||||
}
|
||||
}
|
||||
|
||||
public HttpContext SetValue(HttpContext value)
|
||||
{
|
||||
if (IsRootContext)
|
||||
{
|
||||
return ExchangeRootHttpContext(value);
|
||||
}
|
||||
var prior = _value;
|
||||
_value = value;
|
||||
return prior;
|
||||
}
|
||||
|
||||
#if ASPNET50
|
||||
private const string LogicalDataKey = "__HttpContext_Current__";
|
||||
#elif ASPNETCORE50
|
||||
private static AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();
|
||||
#endif
|
||||
|
||||
private static HttpContext AccessRootHttpContext()
|
||||
{
|
||||
#if ASPNET50
|
||||
var handle = CallContext.LogicalGetData(LogicalDataKey) as ObjectHandle;
|
||||
return handle != null ? handle.Unwrap() as HttpContext : null;
|
||||
#elif ASPNETCORE50
|
||||
return _httpContextCurrent.Value;
|
||||
#else
|
||||
throw new Exception("TODO: CallContext not available");
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
CallContext.LogicalSetData(LogicalDataKey, new ObjectHandle(value));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
private AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();
|
||||
public HttpContext Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return _httpContextCurrent.Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
_httpContextCurrent.Value = value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,9 +7,6 @@ namespace Microsoft.AspNet.Hosting
|
|||
{
|
||||
public interface IHttpContextAccessor
|
||||
{
|
||||
bool IsRootContext { get; set; }
|
||||
HttpContext Value { get; }
|
||||
|
||||
HttpContext SetValue(HttpContext value);
|
||||
HttpContext Value { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,7 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Hosting.Builder;
|
||||
using Microsoft.AspNet.Hosting.Server;
|
||||
|
||||
namespace Microsoft.AspNet.Hosting
|
||||
{
|
||||
|
|
@ -14,16 +12,19 @@ namespace Microsoft.AspNet.Hosting
|
|||
{
|
||||
private readonly IHttpContextFactory _httpContextFactory;
|
||||
private readonly RequestDelegate _requestDelegate;
|
||||
private readonly IHttpContextAccessor _contextAccessor;
|
||||
|
||||
public PipelineInstance(IHttpContextFactory httpContextFactory, RequestDelegate requestDelegate)
|
||||
public PipelineInstance(IHttpContextFactory httpContextFactory, RequestDelegate requestDelegate, IHttpContextAccessor contextAccessor)
|
||||
{
|
||||
_httpContextFactory = httpContextFactory;
|
||||
_requestDelegate = requestDelegate;
|
||||
_contextAccessor = contextAccessor;
|
||||
}
|
||||
|
||||
public Task Invoke(object serverEnvironment)
|
||||
{
|
||||
var httpContext = _httpContextFactory.CreateHttpContext(serverEnvironment);
|
||||
_contextAccessor.Value = 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.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Hosting;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
|
|
@ -13,7 +12,6 @@ namespace Microsoft.AspNet.RequestContainer
|
|||
public RequestServicesContainer(
|
||||
HttpContext context,
|
||||
IServiceScopeFactory scopeFactory,
|
||||
IHttpContextAccessor appContextAccessor,
|
||||
IServiceProvider appServiceProvider)
|
||||
{
|
||||
if (scopeFactory == null)
|
||||
|
|
@ -24,12 +22,6 @@ namespace Microsoft.AspNet.RequestContainer
|
|||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
if (appContextAccessor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(appContextAccessor));
|
||||
}
|
||||
|
||||
AppContextAccessor = appContextAccessor;
|
||||
|
||||
Context = context;
|
||||
PriorAppServices = context.ApplicationServices;
|
||||
|
|
@ -37,23 +29,15 @@ namespace Microsoft.AspNet.RequestContainer
|
|||
|
||||
// Begin the scope
|
||||
Scope = scopeFactory.CreateScope();
|
||||
ScopeContextAccessor = Scope.ServiceProvider.GetRequiredService<IHttpContextAccessor>();
|
||||
|
||||
Context.ApplicationServices = appServiceProvider;
|
||||
Context.RequestServices = Scope.ServiceProvider;
|
||||
|
||||
PriorAppHttpContext = AppContextAccessor.SetValue(context);
|
||||
PriorScopeHttpContext = ScopeContextAccessor.SetValue(context);
|
||||
}
|
||||
|
||||
private HttpContext Context { get; set; }
|
||||
private IServiceProvider PriorAppServices { get; set; }
|
||||
private IServiceProvider PriorRequestServices { get; set; }
|
||||
private HttpContext PriorAppHttpContext { get; set; }
|
||||
private HttpContext PriorScopeHttpContext { 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
|
||||
|
|
@ -73,29 +57,24 @@ namespace Microsoft.AspNet.RequestContainer
|
|||
|
||||
// Matches constructor of RequestContainer
|
||||
var rootServiceProvider = serviceProvider.GetRequiredService<IServiceProvider>();
|
||||
var rootHttpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
|
||||
var rootServiceScopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
|
||||
|
||||
rootHttpContextAccessor.IsRootContext = true;
|
||||
|
||||
// Pre Scope setup
|
||||
var priorApplicationServices = serviceProvider;
|
||||
var priorRequestServices = serviceProvider;
|
||||
|
||||
var appServiceProvider = rootServiceProvider;
|
||||
var appServiceScopeFactory = rootServiceScopeFactory;
|
||||
var appHttpContextAccessor = rootHttpContextAccessor;
|
||||
|
||||
if (priorApplicationServices != null &&
|
||||
priorApplicationServices != appServiceProvider)
|
||||
{
|
||||
appServiceProvider = priorApplicationServices;
|
||||
appServiceScopeFactory = priorApplicationServices.GetRequiredService<IServiceScopeFactory>();
|
||||
appHttpContextAccessor = priorApplicationServices.GetRequiredService<IHttpContextAccessor>();
|
||||
}
|
||||
|
||||
// Creates the scope and does the service swaps
|
||||
return new RequestServicesContainer(httpContext, appServiceScopeFactory, appHttpContextAccessor, appServiceProvider);
|
||||
return new RequestServicesContainer(httpContext, appServiceScopeFactory, appServiceProvider);
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
|
|
@ -107,9 +86,6 @@ namespace Microsoft.AspNet.RequestContainer
|
|||
{
|
||||
if (disposing)
|
||||
{
|
||||
ScopeContextAccessor.SetValue(PriorScopeHttpContext);
|
||||
AppContextAccessor.SetValue(PriorAppHttpContext);
|
||||
|
||||
Context.RequestServices = PriorRequestServices;
|
||||
Context.ApplicationServices = PriorAppServices;
|
||||
}
|
||||
|
|
@ -123,10 +99,6 @@ namespace Microsoft.AspNet.RequestContainer
|
|||
Context = null;
|
||||
PriorAppServices = null;
|
||||
PriorRequestServices = null;
|
||||
ScopeContextAccessor = null;
|
||||
AppContextAccessor = null;
|
||||
PriorAppHttpContext = null;
|
||||
PriorScopeHttpContext = null;
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,23 @@ namespace Microsoft.AspNet.TestHost
|
|||
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]
|
||||
public async Task CreateInvokesApp()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue