Simplify HttpContextAccessor (now singleton)

This commit is contained in:
Hao Kung 2015-01-14 17:24:33 -08:00
parent f786fb7bd0
commit 16a811479e
7 changed files with 46 additions and 81 deletions

View File

@ -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(() =>

View File

@ -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));

View File

@ -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
}
} }
} }

View File

@ -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);
} }
} }

View File

@ -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);
} }

View File

@ -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;
} }

View File

@ -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()
{ {