From 19205f1435b612afaae45507770bdd56b866ee46 Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Mon, 6 Aug 2012 22:40:06 +0600 Subject: [PATCH] Improves perf of UmbracoModule rendering, adds unit test for UmbracoModule, fixes StateHelper with HttpContext.Current, Fixes Action with empty JsSource, Fixes GlobalSettings to be unit testable, adds medium trust config section to my own transform. --- .../Configuration/GlobalSettings.cs | 101 +++++++++++------- src/Umbraco.Tests/BusinessLogic/BaseTest.cs | 1 + src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + src/Umbraco.Tests/UmbracoModuleTests.cs | 67 ++++++++++++ .../web.Template.ShandemVaio.Debug.config | 1 + src/Umbraco.Web.UI/web.Template.config | 3 +- src/Umbraco.Web/Routing/DocumentRequest.cs | 11 +- src/Umbraco.Web/UmbracoApplication.cs.orig | 87 --------------- src/Umbraco.Web/UmbracoModule.cs | 81 +++++++++----- src/umbraco.businesslogic/StateHelper.cs | 15 +-- src/umbraco.cms/Actions/Action.cs | 4 +- 11 files changed, 207 insertions(+), 165 deletions(-) create mode 100644 src/Umbraco.Tests/UmbracoModuleTests.cs delete mode 100644 src/Umbraco.Web/UmbracoApplication.cs.orig diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs index 4d67cc78d6..2ead709124 100644 --- a/src/Umbraco.Core/Configuration/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs @@ -5,6 +5,7 @@ using System.Web; using System.Web.Configuration; using System.Xml; using Umbraco.Core.IO; +using Umbraco.Core.Logging; namespace Umbraco.Core.Configuration { @@ -20,6 +21,35 @@ namespace Umbraco.Core.Configuration /// internal class GlobalSettings { + + private static HttpContextBase _customHttpContext; + + /// + /// Gets/sets the HttpContext object, this is generally used for unit testing. By default this will + /// use the HttpContext.Current + /// + internal static HttpContextBase HttpContext + { + get + { + if (_customHttpContext == null && System.Web.HttpContext.Current != null) + { + //return the current HttpContxt, do NOT store this in the _customHttpContext field + //as it will persist across reqeusts! + return new HttpContextWrapper(System.Web.HttpContext.Current); + } + + if (_customHttpContext == null && System.Web.HttpContext.Current == null) + { + //throw new NullReferenceException("The HttpContext property has not been set or the object execution is not running inside of an HttpContext"); + //NOTE: We should throw an exception here but the legacy code checks for null so we need to stick witht he legacy code for now. + return null; + } + return _customHttpContext; + } + set { _customHttpContext = value; } + } + #region Private static fields // CURRENT UMBRACO VERSION ID @@ -39,7 +69,7 @@ namespace Umbraco.Core.Configuration { get { - if (HttpContext.Current != null) + if (HttpContext != null) return ConfigurationManager.AppSettings["umbracoReservedUrls"]; return String.Empty; } @@ -53,7 +83,7 @@ namespace Umbraco.Core.Configuration { get { - if (HttpContext.Current != null) + if (HttpContext != null) return ConfigurationManager.AppSettings["umbracoReservedPaths"]; return String.Empty; } @@ -337,7 +367,7 @@ namespace Umbraco.Core.Configuration get { int versionCheckPeriod = 7; - if (HttpContext.Current != null) + if (HttpContext != null) { if (int.TryParse(ConfigurationManager.AppSettings["umbracoVersionCheckPeriod"], out versionCheckPeriod)) return versionCheckPeriod; @@ -355,7 +385,7 @@ namespace Umbraco.Core.Configuration { get { - if (HttpContext.Current != null) + if (HttpContext != null) return ConfigurationManager.AppSettings["umbracoUrlForbittenCharacters"]; return ""; } @@ -369,7 +399,7 @@ namespace Umbraco.Core.Configuration { get { - if (HttpContext.Current != null) + if (HttpContext != null) return ConfigurationManager.AppSettings["umbracoUrlSpaceCharacter"]; return ""; } @@ -407,7 +437,7 @@ namespace Umbraco.Core.Configuration { get { - if (HttpContext.Current != null) + if (HttpContext != null) return ConfigurationManager.AppSettings["umbracoDisableXsltExtensions"]; return ""; } @@ -421,7 +451,7 @@ namespace Umbraco.Core.Configuration { get { - if (HttpContext.Current != null) + if (HttpContext != null) return ConfigurationManager.AppSettings["umbracoEditXhtmlMode"]; return ""; } @@ -435,7 +465,7 @@ namespace Umbraco.Core.Configuration { get { - if (HttpContext.Current != null) + if (HttpContext != null) return ConfigurationManager.AppSettings["umbracoDefaultUILanguage"]; return ""; } @@ -449,7 +479,7 @@ namespace Umbraco.Core.Configuration { get { - if (HttpContext.Current != null) + if (HttpContext != null) return ConfigurationManager.AppSettings["umbracoProfileUrl"]; return ""; } @@ -465,7 +495,7 @@ namespace Umbraco.Core.Configuration { get { - if (HttpContext.Current != null) + if (HttpContext != null) return bool.Parse(ConfigurationManager.AppSettings["umbracoHideTopLevelNodeFromPath"]); return false; } @@ -584,7 +614,7 @@ namespace Umbraco.Core.Configuration { string license = "the open source license MIT. The umbraco UI is freeware licensed under the umbraco license."; - if (HttpContext.Current != null) + if (HttpContext != null) { var versionDoc = new XmlDocument(); var versionReader = new XmlTextReader(IOHelper.MapPath(SystemDirectories.Umbraco + "/version.xml")); @@ -624,31 +654,28 @@ namespace Umbraco.Core.Configuration internal static bool Test { get - { - try - { - HttpContext.Current.Response.Write("ContentXML :" + ContentXml + "\n"); - HttpContext.Current.Response.Write("DbDSN :" + DbDsn + "\n"); - HttpContext.Current.Response.Write("DebugMode :" + DebugMode + "\n"); - HttpContext.Current.Response.Write("DefaultUILanguage :" + DefaultUILanguage + "\n"); - HttpContext.Current.Response.Write("VersionCheckPeriod :" + VersionCheckPeriod + "\n"); - HttpContext.Current.Response.Write("DisableXsltExtensions :" + DisableXsltExtensions + "\n"); - HttpContext.Current.Response.Write("EditXhtmlMode :" + EditXhtmlMode + "\n"); - HttpContext.Current.Response.Write("HideTopLevelNodeFromPath :" + HideTopLevelNodeFromPath + "\n"); - HttpContext.Current.Response.Write("Path :" + Path + "\n"); - HttpContext.Current.Response.Write("ProfileUrl :" + ProfileUrl + "\n"); - HttpContext.Current.Response.Write("ReservedPaths :" + ReservedPaths + "\n"); - HttpContext.Current.Response.Write("ReservedUrls :" + ReservedUrls + "\n"); - HttpContext.Current.Response.Write("StorageDirectory :" + StorageDirectory + "\n"); - HttpContext.Current.Response.Write("TimeOutInMinutes :" + TimeOutInMinutes + "\n"); - HttpContext.Current.Response.Write("UrlForbittenCharacters :" + UrlForbittenCharacters + "\n"); - HttpContext.Current.Response.Write("UrlSpaceCharacter :" + UrlSpaceCharacter + "\n"); - HttpContext.Current.Response.Write("UseDirectoryUrls :" + UseDirectoryUrls + "\n"); - return true; - } - catch - { - } + { + if (HttpContext != null) + { + HttpContext.Response.Write("ContentXML :" + ContentXml + "\n"); + HttpContext.Response.Write("DbDSN :" + DbDsn + "\n"); + HttpContext.Response.Write("DebugMode :" + DebugMode + "\n"); + HttpContext.Response.Write("DefaultUILanguage :" + DefaultUILanguage + "\n"); + HttpContext.Response.Write("VersionCheckPeriod :" + VersionCheckPeriod + "\n"); + HttpContext.Response.Write("DisableXsltExtensions :" + DisableXsltExtensions + "\n"); + HttpContext.Response.Write("EditXhtmlMode :" + EditXhtmlMode + "\n"); + HttpContext.Response.Write("HideTopLevelNodeFromPath :" + HideTopLevelNodeFromPath + "\n"); + HttpContext.Response.Write("Path :" + Path + "\n"); + HttpContext.Response.Write("ProfileUrl :" + ProfileUrl + "\n"); + HttpContext.Response.Write("ReservedPaths :" + ReservedPaths + "\n"); + HttpContext.Response.Write("ReservedUrls :" + ReservedUrls + "\n"); + HttpContext.Response.Write("StorageDirectory :" + StorageDirectory + "\n"); + HttpContext.Response.Write("TimeOutInMinutes :" + TimeOutInMinutes + "\n"); + HttpContext.Response.Write("UrlForbittenCharacters :" + UrlForbittenCharacters + "\n"); + HttpContext.Response.Write("UrlSpaceCharacter :" + UrlSpaceCharacter + "\n"); + HttpContext.Response.Write("UseDirectoryUrls :" + UseDirectoryUrls + "\n"); + return true; + } return false; } } @@ -701,7 +728,7 @@ namespace Umbraco.Core.Configuration foreach (string st in _reservedList._list.Keys) res += st + ","; - HttpContext.Current.Trace.Write("umbracoGlobalsettings", "reserverd urls: '" + res + "'"); + LogHelper.Debug("reserverd urls: '" + res + "'"); // return true if url starts with an element of the reserved list return _reservedList.StartsWith(url.ToLower()); diff --git a/src/Umbraco.Tests/BusinessLogic/BaseTest.cs b/src/Umbraco.Tests/BusinessLogic/BaseTest.cs index e97198a898..0fe0cb4059 100644 --- a/src/Umbraco.Tests/BusinessLogic/BaseTest.cs +++ b/src/Umbraco.Tests/BusinessLogic/BaseTest.cs @@ -20,6 +20,7 @@ namespace Umbraco.Tests.BusinessLogic public void Dispose() { ClearDatabase(); + ConfigurationManager.AppSettings.Set("umbracoDbDSN", ""); } /// diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index f836a5788e..4762eef385 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -79,6 +79,7 @@ + diff --git a/src/Umbraco.Tests/UmbracoModuleTests.cs b/src/Umbraco.Tests/UmbracoModuleTests.cs new file mode 100644 index 0000000000..27eb37ec71 --- /dev/null +++ b/src/Umbraco.Tests/UmbracoModuleTests.cs @@ -0,0 +1,67 @@ +using System.Configuration; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web; + +namespace Umbraco.Tests +{ + [TestFixture] + public class UmbracoModuleTests + { + private UmbracoModule _module; + + [SetUp] + public void Initialize() + { + TestHelper.SetupLog4NetForTests(); + ApplicationContext.Current = new ApplicationContext() + { + IsReady = true + }; + _module = new UmbracoModule(); + ConfigurationManager.AppSettings.Set("umbracoConfigurationStatus", Umbraco.Core.Configuration.GlobalSettings.CurrentVersion); + ConfigurationManager.AppSettings.Set("umbracoReservedPaths", "~/umbraco,~/install/"); + ConfigurationManager.AppSettings.Set("umbracoReservedUrls", "~/config/splashes/booting.aspx,~/install/default.aspx,~/config/splashes/noNodes.aspx,~/VSEnterpriseHelper.axd"); + } + + [TearDown] + public void TearDown() + { + _module.Dispose(); + //reset the context on global settings + Umbraco.Core.Configuration.GlobalSettings.HttpContext = null; + //reset the app context + ApplicationContext.Current = null; + //reset the app config + ConfigurationManager.AppSettings.Set("umbracoConfigurationStatus", ""); + ConfigurationManager.AppSettings.Set("umbracoReservedPaths", ""); + ConfigurationManager.AppSettings.Set("umbracoReservedUrls", ""); + } + + [TestCase("/umbraco_client/Tree/treeIcons.css", false)] + [TestCase("/umbraco_client/Tree/Themes/umbraco/style.css?cdv=37", false)] + [TestCase("/umbraco_client/scrollingmenu/style.css?cdv=37", false)] + [TestCase("/umbraco/umbraco.aspx", false)] + [TestCase("/umbraco/editContent.aspx", false)] + [TestCase("/install/default.aspx", false)] + [TestCase("/install/test.aspx", false)] + [TestCase("/base/somebasehandler", false)] + [TestCase("/", true)] + [TestCase("/home.aspx", true)] + public void Ensure_Request_Routable(string url, bool assert) + { + var httpContextFactory = new FakeHttpContextFactory(url); + var httpContext = httpContextFactory.HttpContext; + //set the context on global settings + Umbraco.Core.Configuration.GlobalSettings.HttpContext = httpContext; + var uri = httpContext.Request.Url; + var lpath = uri.AbsolutePath.ToLower(); + + var result = _module.EnsureRequestRoutable(uri, lpath, httpContext); + + Assert.AreEqual(assert, result); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/web.Template.ShandemVaio.Debug.config b/src/Umbraco.Web.UI/web.Template.ShandemVaio.Debug.config index ca20bd8113..8b87c0b713 100644 --- a/src/Umbraco.Web.UI/web.Template.ShandemVaio.Debug.config +++ b/src/Umbraco.Web.UI/web.Template.ShandemVaio.Debug.config @@ -27,6 +27,7 @@ + - - + diff --git a/src/Umbraco.Web/Routing/DocumentRequest.cs b/src/Umbraco.Web/Routing/DocumentRequest.cs index 3f54cf5866..6806861d96 100644 --- a/src/Umbraco.Web/Routing/DocumentRequest.cs +++ b/src/Umbraco.Web/Routing/DocumentRequest.cs @@ -15,12 +15,15 @@ using umbraco.cms.businesslogic.member; using umbraco.cms.businesslogic.language; namespace Umbraco.Web.Routing { - // represents a request for one specified Umbraco document to be rendered - // by one specified template, using one particular culture. - // + + + + /// + /// represents a request for one specified Umbraco document to be rendered + /// by one specified template, using one particular culture. + /// public class DocumentRequest { - public DocumentRequest(Uri uri, RoutingContext routingContext) { this.Uri = uri; diff --git a/src/Umbraco.Web/UmbracoApplication.cs.orig b/src/Umbraco.Web/UmbracoApplication.cs.orig deleted file mode 100644 index 402929c4c2..0000000000 --- a/src/Umbraco.Web/UmbracoApplication.cs.orig +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Web.Mvc; -using Umbraco.Core; -using Umbraco.Web.Routing; -using umbraco.businesslogic; - -namespace Umbraco.Web -{ - /// - /// The Umbraco global.asax class - /// - public class UmbracoApplication : System.Web.HttpApplication - { - public static event EventHandler ApplicationStarting; - - public static event EventHandler ApplicationStarted; - - /// - /// Initializes the Umbraco application - /// - /// - /// - protected void Application_Start(object sender, EventArgs e) - { - using (DisposableTimer.TraceDuration( - "Umbraco application starting", - "Umbraco application startup complete")) - { - // Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK] - ClientDependency.Core.CompositeFiles.Providers.XmlFileMapper.FileMapVirtualFolder = "~/App_Data/TEMP/ClientDependency"; - ClientDependency.Core.CompositeFiles.Providers.BaseCompositeFileProcessingProvider.UrlTypeDefault = ClientDependency.Core.CompositeFiles.Providers.CompositeUrlType.Base64QueryStrings; - - //create the ApplicationContext - ApplicationContext.Current = new ApplicationContext() - { - IsReady = true // fixme - }; - - //find and initialize the application startup handlers - ApplicationStartupHandler.RegisterHandlers(); - - OnApplicationStarting(sender, e); - - //all resolvers are now frozen and cannot be modified - Umbraco.Core.Resolving.Resolution.Freeze(); - - OnApplicationStarted(sender, e); - } - } - - /// - /// Developers can override this method to modify objects on startup - /// - /// - /// - protected virtual void OnApplicationStarting(object sender, EventArgs e) - { - if (ApplicationStarting != null) - ApplicationStarting(sender, e); - } - - /// - /// Developers can override this method to do anything they need to do once the application startup routine is completed. - /// - /// - /// - protected virtual void OnApplicationStarted(object sender, EventArgs e) - { - if (ApplicationStarted != null) - ApplicationStarted(sender, e); - } - - protected virtual void Application_Error(object sender, EventArgs e) - { - - } - - protected virtual void Application_End(object sender, EventArgs e) - { - - } - } -} diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 428f6ecd2e..b6b7f4e09a 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -22,6 +22,31 @@ namespace Umbraco.Web public class UmbracoModule : IHttpModule { + /// + /// Checks the current request and ensures that it is routable based on the structure of the request and URI + /// + /// + /// + /// + /// + internal bool EnsureRequestRoutable(Uri uri, string lpath, HttpContextBase httpContext) + { + // ensure this is a document request + if (!EnsureDocumentRequest(httpContext, uri, lpath)) + return false; + // ensure Umbraco is ready to serve documents + if (!EnsureIsReady(httpContext, uri)) + return false; + // ensure Umbraco is properly configured to serve documents + if (!EnsureIsConfigured(httpContext, uri)) + return false; + // ensure that its not a base rest handler + if ((UmbracoSettings.EnableBaseRestHandler) && !EnsureNotBaseRestHandler(lpath)) + return false; + + return true; + } + /// /// Entry point for a request /// @@ -30,14 +55,17 @@ namespace Umbraco.Web { LogHelper.Debug("Start processing request"); - //TODO: We need to ensure the below only executes for real requests (i.e. not css, favicon, etc...) - // I'm pretty sure we need to bind to the PostHandlerAssigned (or whatever event) and follow the same - // practices that is in umbraMVCo - var uri = httpContext.Request.Url; var lpath = uri.AbsolutePath.ToLower(); + //do not continue if this request is not routablehttpContextFactory + if (!EnsureRequestRoutable(uri, lpath, httpContext)) + { + LogHelper.Debug("End processing request, not transfering to handler"); + return; + } + // add Umbraco's signature header if (!UmbracoSettings.RemoveUmbracoVersionHeader) httpContext.Response.AddHeader("X-Umbraco-Version", string.Format("{0}.{1}", GlobalSettings.VersionMajor, GlobalSettings.VersionMinor)); @@ -86,27 +114,20 @@ namespace Umbraco.Web // // to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors // so that they point to a non-existing page eg /redirect-404.aspx - - var ok = true; - - // ensure this is a document request - ok = ok && EnsureDocumentRequest(httpContext, uri, lpath); - // ensure Umbraco is ready to serve documents - ok = ok && EnsureIsReady(httpContext, uri); - // ensure Umbraco is properly configured to serve documents - ok = ok && EnsureIsConfigured(httpContext, uri); - ok = ok && (!UmbracoSettings.EnableBaseRestHandler || EnsureNotBaseRestHandler(httpContext, lpath)); - ok = ok && (EnsureNotBaseRestHandler(httpContext, lpath)); - - if (!ok) - { - LogHelper.Debug("End processing request, not transfering to handler"); - return; - } + // legacy - no idea what this is LegacyCleanUmbPageFromQueryString(ref uri, ref lpath); + + //TODO: Before this happens, we need to : + // * move all of this logic into a custom IHttpHandler (maybe) + // ** This is because of TransferRequest actual makes an internal request and the HttpContext is reset + // which means that all HttpContext.Items setting need to occur after + // * need to figure out if we can execute default.aspx as a handler from our IHttpHandler instead of rewriting to it + // * same goes for MVC + // * perhaps its even possible to do all of this without any Rewriting or TransferRequest! + //**THERE** we should create the doc request // before, we're not sure we handling a doc request docreq.LookupDomain(); @@ -120,7 +141,7 @@ namespace Umbraco.Web if (docreq.Is404) httpContext.Response.StatusCode = 404; - TransferRequest("~/default.aspx" + docreq.Uri.Query); + TransferRequest("~/default.aspx" + docreq.Uri.Query, httpContext); // it is up to default.aspx to figure out what to display in case // there is no document (ugly 404 page?) or no template (blank page?) @@ -216,7 +237,7 @@ namespace Umbraco.Web // fixme ?orgurl=... ?retry=... } - TransferRequest(bootUrl); + TransferRequest(bootUrl, httpContext); return false; } @@ -242,7 +263,7 @@ namespace Umbraco.Web // checks if the current request is a /base REST handler request // returns false if it is, otherwise true - bool EnsureNotBaseRestHandler(HttpContextBase httpContext, string lpath) + bool EnsureNotBaseRestHandler(string lpath) { // the /base REST handler still lives in umbraco.dll and has // not been refactored at the moment. it still is a module, @@ -263,7 +284,7 @@ namespace Umbraco.Web } // transfers the request using the fastest method available on the server - void TransferRequest(string path) + void TransferRequest(string path, HttpContextBase httpContext) { LogHelper.Debug("Transfering to " + path); @@ -288,9 +309,9 @@ namespace Umbraco.Web // http://forums.iis.net/t/1146511.aspx if (integrated) - HttpContext.Current.Server.TransferRequest(path); + httpContext.Server.TransferRequest(path); else - HttpContext.Current.RewritePath(path); + httpContext.RewritePath(path); } @@ -350,7 +371,11 @@ namespace Umbraco.Web { // used to be done in PostAuthorizeRequest but then it disabled OutputCaching due // to rewriting happening too early in the chain (Alex Norcliffe 2010-02). - app.PostResolveRequestCache += (sender, e) => + //app.PostResolveRequestCache += (sender, e) => + + //SD: changed to post map request handler so we can know what the handler actually is, this is a better fit for + //when we handle the routing + app.PostMapRequestHandler += (sender, e) => { var httpContext = ((HttpApplication)sender).Context; ProcessRequest(new HttpContextWrapper(httpContext)); diff --git a/src/umbraco.businesslogic/StateHelper.cs b/src/umbraco.businesslogic/StateHelper.cs index 3884549f3f..607a729634 100644 --- a/src/umbraco.businesslogic/StateHelper.cs +++ b/src/umbraco.businesslogic/StateHelper.cs @@ -11,7 +11,7 @@ namespace umbraco.BusinessLogic public class StateHelper { - private static HttpContextBase _httpContext; + private static HttpContextBase _customHttpContext; /// /// Gets/sets the HttpContext object, this is generally used for unit testing. By default this will @@ -21,17 +21,20 @@ namespace umbraco.BusinessLogic { get { - if (_httpContext == null && System.Web.HttpContext.Current != null) + if (_customHttpContext == null && System.Web.HttpContext.Current != null) { - _httpContext = new HttpContextWrapper(System.Web.HttpContext.Current); + //return the current HttpContxt, do NOT store this in the _customHttpContext field + //as it will persist across reqeusts! + return new HttpContextWrapper(System.Web.HttpContext.Current); } - else if (_httpContext == null && System.Web.HttpContext.Current == null) + + if (_customHttpContext == null && System.Web.HttpContext.Current == null) { throw new NullReferenceException("The HttpContext property has not been set or the object execution is not running inside of an HttpContext"); } - return _httpContext; + return _customHttpContext; } - set { _httpContext = value; } + set { _customHttpContext = value; } } #region Session Helpers diff --git a/src/umbraco.cms/Actions/Action.cs b/src/umbraco.cms/Actions/Action.cs index d7d290b66f..b86379ce71 100644 --- a/src/umbraco.cms/Actions/Action.cs +++ b/src/umbraco.cms/Actions/Action.cs @@ -148,7 +148,9 @@ namespace umbraco.BusinessLogic.Actions /// public static List GetJavaScriptFileReferences() { - return ActionsResolver.Current.Actions.Select(x => x.JsSource).ToList(); + return ActionsResolver.Current.Actions + .Where(x => !string.IsNullOrWhiteSpace(x.JsSource)) + .Select(x => x.JsSource).ToList(); //return ActionJsReference; }