2

我正在尝试将几个 javascript 文件捆绑在一起,但我还需要在 js 中包含来自 app.config 的变量。

我的想法是使用控制器返回一个字符串来设置变量,所以去 ~/Javascript/Index 会返回 var foo = "bar"; 这工作正常。

但是当我尝试构建一个包时,静态文件被包含在内,但字符串(或视图)没有显示出来。环顾四周,我发现优化框架仅限于静态文件,直到 1.1 实现了对 VirtualPathProviders 的支持。

我升级到了最新的包,但是我找不到任何关于如何混合静态文件和控制器/视图生成的文件来捆绑的信息。我想我只想使用 MVC 路径提供程序?

我的另一个想法是尝试使用捆绑引擎构建捆绑静态文件的字符串,然后附加我的字符​​串并将其全部返回给浏览器。但是,我找不到允许我使用捆绑引擎返回捆绑过程结果的方法。

4

1 回答 1

4

将动态内容集成到捆绑过程中需要以下步骤:

  1. 编写请求/构建所需内容的逻辑。直接从 Controller 生成内容需要一些工作:

    public static class ControllerActionHelper
    {
        public static string RenderControllerActionToString(string virtualPath)
        {
            HttpContext httpContext = CreateHttpContext(virtualPath);
            HttpContextWrapper httpContextWrapper = new HttpContextWrapper(httpContext);
    
            RequestContext httpResponse = new RequestContext()
            {
                HttpContext = httpContextWrapper,
                RouteData = RouteTable.Routes.GetRouteData(httpContextWrapper)
            };
    
            // Set HttpContext.Current if RenderActionToString is called outside of a request
            if (HttpContext.Current == null)
            {
                HttpContext.Current = httpContext;
            }
    
            IControllerFactory controllerFactory = ControllerBuilder.Current.GetControllerFactory();
            IController controller = controllerFactory.CreateController(httpResponse,
                httpResponse.RouteData.GetRequiredString("controller"));
            controller.Execute(httpResponse);
    
            return httpResponse.HttpContext.Response.Output.ToString();
        }
    
        private static HttpContext CreateHttpContext(string virtualPath)
        {
            HttpRequest httpRequest = new HttpRequest(string.Empty, ToDummyAbsoluteUrl(virtualPath), string.Empty);
            HttpResponse httpResponse = new HttpResponse(new StringWriter());
    
            return new HttpContext(httpRequest, httpResponse);
        }
    
        private static string ToDummyAbsoluteUrl(string virtualPath)
        {
            return string.Format("http://dummy.net{0}", VirtualPathUtility.ToAbsolute(virtualPath));
        }
    }
    
  2. 实现一个虚拟路径提供者,它包装现有的路径并拦截所有应该传递动态内容的虚拟路径。

    public class ControllerActionVirtualPathProvider : VirtualPathProvider
    {
        public ControllerActionVirtualPathProvider(VirtualPathProvider virtualPathProvider)
        {
            // Wrap an existing virtual path provider
            VirtualPathProvider = virtualPathProvider;
        }
    
        protected VirtualPathProvider VirtualPathProvider { get; set; }
    
        public override string CombineVirtualPaths(string basePath, string relativePath)
        {
            return VirtualPathProvider.CombineVirtualPaths(basePath, relativePath);
        }
    
        public override bool DirectoryExists(string virtualDir)
        {
            return VirtualPathProvider.DirectoryExists(virtualDir);
        }
    
        public override bool FileExists(string virtualPath)
        {
            if (ControllerActionHelper.IsControllerActionRoute(virtualPath))
            {
                return true;
            }
    
            return VirtualPathProvider.FileExists(virtualPath);
        }
    
        public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies,
            DateTime utcStart)
        {
            AggregateCacheDependency aggregateCacheDependency = new AggregateCacheDependency();
    
            List<string> virtualPathDependenciesCopy = virtualPathDependencies.Cast<string>().ToList();
    
            // Create CacheDependencies for our virtual Controller Action paths
            foreach (string virtualPathDependency in virtualPathDependenciesCopy.ToList())
            {
                if (ControllerActionHelper.IsControllerActionRoute(virtualPathDependency))
                {
                    aggregateCacheDependency.Add(new ControllerActionCacheDependency(virtualPathDependency));
                    virtualPathDependenciesCopy.Remove(virtualPathDependency);
                }
            }
    
            // Aggregate them with the base cache dependency for virtual file paths
            aggregateCacheDependency.Add(VirtualPathProvider.GetCacheDependency(virtualPath, virtualPathDependenciesCopy,
                utcStart));
    
            return aggregateCacheDependency;
        }
    
        public override string GetCacheKey(string virtualPath)
        {
            return VirtualPathProvider.GetCacheKey(virtualPath);
        }
    
        public override VirtualDirectory GetDirectory(string virtualDir)
        {
            return VirtualPathProvider.GetDirectory(virtualDir);
        }
    
        public override VirtualFile GetFile(string virtualPath)
        {
            if (ControllerActionHelper.IsControllerActionRoute(virtualPath))
            {
                return new ControllerActionVirtualFile(virtualPath,
                    new MemoryStream(Encoding.Default.GetBytes(ControllerActionHelper.RenderControllerActionToString(virtualPath))));
            }
    
            return VirtualPathProvider.GetFile(virtualPath);
        }
    
        public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
        {
            return VirtualPathProvider.GetFileHash(virtualPath, virtualPathDependencies);
        }
    
        public override object InitializeLifetimeService()
        {
            return VirtualPathProvider.InitializeLifetimeService();
        }
    }
    
    public class ControllerActionVirtualFile : VirtualFile
    {
        public CustomVirtualFile (string virtualPath, Stream stream)
            : base(virtualPath)
        {
            Stream = stream;
        }
    
        public Stream Stream { get; private set; }
    
        public override Stream Open()
        {
             return Stream;
        }
    }
    

    如果需要,还必须实现 CacheDependency:

    public class ControllerActionCacheDependency : CacheDependency
    {
        public ControllerActionCacheDependency(string virtualPath, int actualizationTime = 10000)
        {
            VirtualPath = virtualPath;
            LastContent = GetContentFromControllerAction();
    
            Timer = new Timer(CheckDependencyCallback, this, actualizationTime, actualizationTime);
        }
    
        private string LastContent { get; set; }
    
        private Timer Timer { get; set; }
    
        private string VirtualPath { get; set; }
    
        protected override void DependencyDispose()
        {
            if (Timer != null)
            {
                Timer.Dispose();
            }
    
            base.DependencyDispose();
        }
    
        private void CheckDependencyCallback(object sender)
        {
            if (Monitor.TryEnter(Timer))
            {
                try
                {
                    string contentFromAction = GetContentFromControllerAction();
    
                    if (contentFromAction != LastContent)
                    {
                        LastContent = contentFromAction;
                        NotifyDependencyChanged(sender, EventArgs.Empty);
                    }
                }
                finally
                {
                    Monitor.Exit(Timer);
                }
            }
        }
    
        private string GetContentFromControllerAction()
        {
            return ControllerActionHelper.RenderControllerActionToString(VirtualPath);
        }
    }
    
  3. 注册您的虚拟路径提供程序:

    public static void RegisterBundles(BundleCollection bundles)
    {
        // Set the virtual path provider
        BundleTable.VirtualPathProvider = new ControllerActionVirtualPathProvider(BundleTable.VirtualPathProvider);
    
        bundles.Add(new Bundle("~/bundle")
            .Include("~/Content/static.js")
            .Include("~/JavaScript/Route1")
            .Include("~/JavaScript/Route2"));
    }
    
  4. 可选:将 Intellisense 支持添加到您的视图中。在您的视图中使用<script>标签并让它们被自定义 ViewResult 删除:

    public class DynamicContentViewResult : ViewResult
    {
        public DynamicContentViewResult()
        {
            StripTags = false;
        }
    
        public string ContentType { get; set; }
    
        public bool StripTags { get; set; }
    
        public string TagName { get; set; }
    
        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
    
            if (string.IsNullOrEmpty(ViewName))
            {
                ViewName = context.RouteData.GetRequiredString("action");
            }
    
            ViewEngineResult result = null;
    
            if (View == null)
            {
                result = FindView(context);
                View = result.View;
            }
    
            string viewResult;
    
            using (StringWriter viewContentWriter = new StringWriter())
            {
                ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, viewContentWriter);
    
                View.Render(viewContext, viewContentWriter);
    
                if (result != null)
                {
                    result.ViewEngine.ReleaseView(context, View);
                }
    
                viewResult = viewContentWriter.ToString();
    
                // Strip Tags
                if (StripTags)
                {
                    string regex = string.Format("<{0}[^>]*>(.*?)</{0}>", TagName);
                    Match res = Regex.Match(viewResult, regex,
                        RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline | RegexOptions.Singleline);
    
                    if (res.Success && res.Groups.Count > 1)
                    {
                        viewResult = res.Groups[1].Value;
                    }
                    else
                    {
                        throw new InvalidProgramException(
                            string.Format("Dynamic content produced by View '{0}' expected to be wrapped in '{1}' tag.", ViewName, TagName));
                    }
                }
            }
    
            context.HttpContext.Response.ContentType = ContentType;
            context.HttpContext.Response.Output.Write(viewResult);
        }
    }
    

    使用扩展方法或向控制器添加辅助函数:

    public static DynamicContentViewResult JavaScriptView(this Controller controller, string viewName, string masterName, object model)
    {
        if (model != null)
        {
            controller.ViewData.Model = model;
        }
    
        return new DynamicContentViewResult
        {
            ViewName = viewName,
            MasterName = masterName,
            ViewData = controller.ViewData,
            TempData = controller.TempData,
            ViewEngineCollection = controller.ViewEngineCollection,
            ContentType = "text/javascript",
            TagName = "script",
            StripTags = true
        };
    }
    

其他类型的动态内容的步骤与此类似。例如,请参阅捆绑和缩小以及嵌入式资源

如果您想尝试一下,我在GitHub 上添加了一个概念证明存储库。

于 2014-12-16T16:05:16.960 回答