#5306: Implemented a background HttpContext factory.

This change instantiates an actual HttpContext object during background sweeps so that Razor templates using Html Helpers (that require HttpContext.Current to be not-null) won't fai lwhen being rendered.
This commit is contained in:
Sipke Schoorstra
2015-05-22 15:37:51 +02:00
parent 3e55c1649e
commit ef2abe14cb
6 changed files with 88 additions and 36 deletions

View File

@@ -0,0 +1,38 @@
using System.IO;
using System.Web;
using Orchard.Settings;
namespace Orchard {
/// <summary>
/// A factory class that creates an HttpContext instance and initializes the HttpContext.Current property with that instance.
/// This is useful when rendering views from a background thread, as some Html Helpers access HttpContext.Current directly, thus preventing a NullReferenceException.
/// </summary>
public class BackgroundHttpContextFactory : IBackgroundHttpContextFactory {
public const string IsBackgroundHttpContextKey = "IsBackgroundHttpContext";
private readonly ISiteService _siteService;
public BackgroundHttpContextFactory(ISiteService siteService) {
_siteService = siteService;
}
public HttpContext CreateHttpContext() {
var url = _siteService.GetSiteSettings().BaseUrl;
var httpContext = new HttpContext(new HttpRequest("", url, ""), new HttpResponse(new StringWriter()));
httpContext.Items[IsBackgroundHttpContextKey] = true;
return httpContext;
}
public void InitializeHttpContext() {
if (HttpContext.Current != null)
return;
HttpContext.Current = CreateHttpContext();
}
}
public interface IBackgroundHttpContextFactory : IDependency {
HttpContext CreateHttpContext();
void InitializeHttpContext();
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Web;
namespace Orchard.Mvc {
public class HttpContextAccessor : IHttpContextAccessor {
private HttpContextBase _httpContext;
public HttpContextBase Current() {
var httpContext = GetStaticProperty();
return !IsBackgroundHttpContext(httpContext) ? new HttpContextWrapper(httpContext) : _httpContext;
}
public void Set(HttpContextBase httpContext) {
_httpContext = httpContext;
}
private static bool IsBackgroundHttpContext(HttpContext httpContext) {
return httpContext == null || httpContext.Items.Contains(BackgroundHttpContextFactory.IsBackgroundHttpContextKey);
}
private static HttpContext GetStaticProperty() {
var httpContext = HttpContext.Current;
if (httpContext == null) {
return null;
}
try {
// The "Request" property throws at application startup on IIS integrated pipeline mode.
if (httpContext.Request == null) {
return null;
}
}
catch (Exception) {
return null;
}
return httpContext;
}
}
}

View File

@@ -1,39 +1,8 @@
using System;
using System.Web;
using System.Web;
namespace Orchard.Mvc {
public interface IHttpContextAccessor {
HttpContextBase Current();
void Set(HttpContextBase httpContext);
}
public class HttpContextAccessor : IHttpContextAccessor {
private HttpContextBase _httpContext;
public HttpContextBase Current() {
var httpContext = GetStaticProperty();
return httpContext != null ? new HttpContextWrapper(httpContext) : _httpContext;
}
public void Set(HttpContextBase httpContext) {
_httpContext = httpContext;
}
private HttpContext GetStaticProperty() {
var httpContext = HttpContext.Current;
if (httpContext == null) {
return null;
}
try {
if (httpContext.Request == null) {
return null;
}
}
catch (Exception) {
return null;
}
return httpContext;
}
}
}

View File

@@ -28,7 +28,7 @@ namespace Orchard.Mvc {
return false;
try {
// The "Request" property throws at application startup on IIS integrated pipeline mode
// The "Request" property throws at application startup on IIS integrated pipeline mode.
var req = HttpContext.Current.Request;
}
catch (Exception) {

View File

@@ -317,6 +317,7 @@
<Compile Include="Mvc\DataAnnotations\LocalizedRequiredAttribute.cs" />
<Compile Include="Mvc\Extensions\RouteExtension.cs" />
<Compile Include="Mvc\FormValueRequiredAttribute.cs" />
<Compile Include="Mvc\HttpContextAccessor.cs" />
<Compile Include="Mvc\HttpContextWorkContext.cs" />
<Compile Include="Mvc\Extensions\ControllerExtensions.cs" />
<Compile Include="Mvc\IOrchardViewPage.cs" />
@@ -644,6 +645,7 @@
<Compile Include="WebApi\Filters\OrchardApiActionFilterDispatcher.cs" />
<Compile Include="WebApi\Routes\IHttpRouteProvider.cs" />
<Compile Include="WebApi\Routes\StandardExtensionHttpRouteProvider.cs" />
<Compile Include="BackgroundHttpContext.cs" />
<Compile Include="WorkContextExtensions.cs" />
<Compile Include="Mvc\ViewEngines\Razor\RazorCompilationEventsShim.cs" />
<Compile Include="Mvc\ViewEngines\Razor\RazorViewEngineProvider.cs" />

View File

@@ -16,24 +16,28 @@ namespace Orchard.Tasks {
public class BackgroundService : IBackgroundService {
private readonly IEnumerable<IBackgroundTask> _tasks;
private readonly ITransactionManager _transactionManager;
private readonly IBackgroundHttpContextFactory _backgroundHttpContextFactory;
private readonly string _shellName;
private readonly IContentManager _contentManager;
public BackgroundService(
IEnumerable<IBackgroundTask> tasks,
ITransactionManager transactionManager,
ShellSettings shellSettings,
IContentManager contentManager) {
IContentManager contentManager,
IBackgroundHttpContextFactory backgroundHttpContextFactory) {
_tasks = tasks;
_transactionManager = transactionManager;
_backgroundHttpContextFactory = backgroundHttpContextFactory;
_shellName = shellSettings.Name;
_contentManager = contentManager;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void Sweep() {
_backgroundHttpContextFactory.InitializeHttpContext();
foreach(var task in _tasks) {
var taskName = task.GetType().FullName;