9

動的にJavascriptを生成するサイトがあります。生成されたコードは、クライアントがサーバーのサービスを簡単に消費できるように、型メタデータといくつかのサーバー側の定数を記述しているため、キャッシュ可能です。ASP.NETバンドル/縮小:動的に生成されたJavascriptを含む

生成されたJavascriptは、ASP.NET MVCコントローラによって処理されます。それにはウリがあります。たとえば~/MyGeneratedJsとします。

私はこのJavascriptを他の静的なJavascriptファイル(例:jQueryなど)と一緒に含めたいと思っています。静的なファイルと同じように、デバッグモードで個別に参照され、非デバッグモードのファイル。

動的に生成されたJavascriptをバンドルに含めるにはどうすればよいですか?

答えて

4

ダーリンが正しいです。現在、バンドルは静的ファイルでのみ動作します。しかし、最新のコンテンツを含むプレースホルダファイルを追加できる場合、バンドルは、プレースホルダファイルが変更されたときに自動的に検出されるファイル変更通知をセットアップします。

また、VirtualPathProvidersをすぐに使用して、動的に生成されたコンテンツを提供する方法を検討していきます。

更新: 1.1-アルファ1のリリースでは、これが可能になりましたVirtualPathProvidersでVPP

+0

それはいいね!あなたはこれらの開発のために従うことをお勧めするブログやニュースサイトを持っていますか? –

+0

私たちはコードプレックスのサイトを公開しましたが、コードはまだありませんが、これはおそらく長期的なものです:http://aspnetoptimization.codeplex.com –

+1

プレースホルダーファイルの意味を理解していません。ダイナミックなmvcルートと同じパスのディスク上にファイルがある場合、それは返され、私のアクションは決して実行されません。私は何が欠けていますか? –

3

これはできません。バンドルは静的ファイルでのみ動作します。

+0

必要に応じてプレースホルダファイルを追加しても問題ありません。コンテンツをサーバーサイドコードと自動的に同期させたいだけです。 –

5

をサポートしていた、今出ています。バンドルプロセスに動的コンテンツを統合するには、次のステップが必要です。

  1. 必要なコンテンツを要求/構築するロジックを記述する。

    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. オプション:あなたの意見にインテリセンスのサポートを追加します。あなたのビュー内<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 
        }; 
    } 
    

手順は似ています他の種類の動的コンテンツの場合たとえば、Bundling and Minification and Embedded Resourcesを参照してください。

試してみたい場合は、GitHubにコンセプト証明リポジトリを追加しました。

関連する問題