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

View File

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

View File

@ -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;
var handle = CallContext.LogicalGetData(LogicalDataKey) as ObjectHandle;
return handle != null ? handle.Unwrap() as HttpContext : null;
}
}
public HttpContext SetValue(HttpContext value)
{
if (IsRootContext)
set
{
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
private static AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();
#endif
private static HttpContext AccessRootHttpContext()
private AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();
public HttpContext Value
{
#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
get
{
return _httpContextCurrent.Value;
}
set
{
_httpContextCurrent.Value = 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;
#endif
}
}
}
}

View File

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

View File

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

View File

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

View File

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