From ef2abe14cbb35b3f42c8a1b6f9c606b1c299071d Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 22 May 2015 15:37:51 +0200 Subject: [PATCH] #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. --- src/Orchard/BackgroundHttpContext.cs | 38 ++++++++++++++++++++++++ src/Orchard/Mvc/HttpContextAccessor.cs | 39 +++++++++++++++++++++++++ src/Orchard/Mvc/IHttpContextAccessor.cs | 33 +-------------------- src/Orchard/Mvc/MvcModule.cs | 2 +- src/Orchard/Orchard.Framework.csproj | 2 ++ src/Orchard/Tasks/BackgroundService.cs | 10 +++++-- 6 files changed, 88 insertions(+), 36 deletions(-) create mode 100644 src/Orchard/BackgroundHttpContext.cs create mode 100644 src/Orchard/Mvc/HttpContextAccessor.cs diff --git a/src/Orchard/BackgroundHttpContext.cs b/src/Orchard/BackgroundHttpContext.cs new file mode 100644 index 000000000..1b4f68f3b --- /dev/null +++ b/src/Orchard/BackgroundHttpContext.cs @@ -0,0 +1,38 @@ +using System.IO; +using System.Web; +using Orchard.Settings; + +namespace Orchard { + /// + /// 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. + /// + 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(); + } +} diff --git a/src/Orchard/Mvc/HttpContextAccessor.cs b/src/Orchard/Mvc/HttpContextAccessor.cs new file mode 100644 index 000000000..615d527df --- /dev/null +++ b/src/Orchard/Mvc/HttpContextAccessor.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/src/Orchard/Mvc/IHttpContextAccessor.cs b/src/Orchard/Mvc/IHttpContextAccessor.cs index 91208d314..462887fee 100644 --- a/src/Orchard/Mvc/IHttpContextAccessor.cs +++ b/src/Orchard/Mvc/IHttpContextAccessor.cs @@ -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; - } - } } diff --git a/src/Orchard/Mvc/MvcModule.cs b/src/Orchard/Mvc/MvcModule.cs index 1e0c49ce6..e1e8c19eb 100644 --- a/src/Orchard/Mvc/MvcModule.cs +++ b/src/Orchard/Mvc/MvcModule.cs @@ -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) { diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index a7093e72b..65099a4cc 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -317,6 +317,7 @@ + @@ -644,6 +645,7 @@ + diff --git a/src/Orchard/Tasks/BackgroundService.cs b/src/Orchard/Tasks/BackgroundService.cs index 8956595bd..b952a6a26 100644 --- a/src/Orchard/Tasks/BackgroundService.cs +++ b/src/Orchard/Tasks/BackgroundService.cs @@ -16,24 +16,28 @@ namespace Orchard.Tasks { public class BackgroundService : IBackgroundService { private readonly IEnumerable _tasks; private readonly ITransactionManager _transactionManager; + private readonly IBackgroundHttpContextFactory _backgroundHttpContextFactory; private readonly string _shellName; - private readonly IContentManager _contentManager; public BackgroundService( IEnumerable 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;