Asp.net-Mvc
ASP.NET 捆綁/縮小:包括動態生成的 Javascript
我有一個動態生成 Javascript 的網站。生成的程式碼描述了類型元數據和一些伺服器端常量,以便客戶端可以輕鬆地使用伺服器的服務——因此它是非常可記憶體的。
生成的 Javascript 由 ASP.NET MVC 控制器提供服務;所以它有一個 Uri;說
~/MyGeneratedJs。我想將此 Javascript 包含在與其他靜態 Javascript 文件(例如 jQuery 等)一起的 Javascript 包中:所以就像靜態文件一樣,我希望它在調試模式下單獨引用,並以縮小形式與其他文件捆綁在非調試模式。
如何在捆綁包中包含動態生成的 Javascript?
Darin 是對的,目前捆綁僅適用於靜態文件。但是,如果您可以添加具有最新內容的佔位符文件,則捆綁會設置文件更改通知,該通知將在佔位符文件更改時自動檢測。
此外,我們很快將轉向使用 VirtualPathProviders,這可能是一種提供動態生成內容的方式。
**更新:**支持 VPP 的 1.1-alpha1 版本現已發布
有了
VirtualPathProviders這個現在是可能的。將動態內容集成到捆綁過程中需要以下步驟:
- 編寫請求/建構所需內容的邏輯。直接從 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)); } }
- 實現一個虛擬路徑提供者,它包裝現有的路徑並攔截所有應該傳遞動態內容的虛擬路徑。
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); } }
- 註冊您的虛擬路徑提供程序:
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")); }
- 可選:將 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 上添加了一個概念證明儲存庫。