9

Mam witrynę, która dynamicznie generuje Javascript. Wygenerowany kod opisuje metadane typu i niektóre stałe po stronie serwera, aby klienci mogli w łatwy sposób korzystać z usług serwera - dzięki czemu jest on bardzo łatwy do buforowania.Łączenie/minimalizowanie ASP.NET: w tym dynamicznie generowane JavaScript

Wygenerowany skrypt JavaScript jest obsługiwany przez kontroler ASP.NET MVC; więc ma Uri; powiedz ~/MyGeneratedJs.

Chciałbym dołączyć tę Javascript w pakiecie JavaScript z innymi statycznymi plikami JavaScript (np. JQuery itp.): Tak jak pliki statyczne chcę, aby były one przywoływane osobno w trybie debugowania i w postaci minilowanej w pakiecie z innymi pliki w trybie bez debugowania.

Jak dołączyć dynamicznie wygenerowany skrypt JavaScript do pakietu?

Odpowiedz

4

Darin ma rację, obecnie pakietowanie działa tylko w przypadku plików statycznych. Ale jeśli możesz dodać plik zastępczy z aktualną treścią, sprzedaż wiązana wykonuje powiadomienia o zmianach w plikach konfiguracyjnych, które będą automatycznie wykrywane po zmianie pliku zastępczego.

Niedługo będziemy też korzystać z VirtualPathProviders, które mogą być sposobem udostępniania dynamicznie generowanych treści.

Aktualizacja: 1,1-alfa1 wydanie jest się teraz który posiada wsparcie dla VPP

+0

Brzmi nieźle! Czy masz bloga lub jakąś witrynę z wiadomościami, które poleciłbyś śledzić w przypadku tego rodzaju wydarzeń? –

+0

Po prostu upubliczniliśmy naszą witrynę codeplex, kod jeszcze nie istnieje, ale jest to prawdopodobnie to, czego chcesz na dłuższą metę: http://aspnetoptimization.codeplex.com –

+1

tak naprawdę nie rozumiesz, co masz na myśli przez plik zastępczy. Jeśli mam plik na dysku o tej samej ścieżce co moja dynamiczna trasa mvc, zostanie on zwrócony, a moje działanie nigdy nie zostanie wykonane. czego mi brakuje? –

3

To nie jest możliwe. Pakiety działają tylko z plikami statycznymi.

+0

Mam problem z dodaniem pliku zastępczego, jeśli to konieczne - chcę tylko, aby zawartość była automatycznie zsynchronizowana z kodem po stronie serwera. –

5

Z VirtualPathProviders jest to teraz możliwe. Integracja zawartości dynamicznej w procesie pakietowania wymaga następujących kroków:

  1. Pisanie logiki żądającej/budującej wymaganą treść. Generowanie treści z kontrolerem bezpośrednio wymaga trochę pracy:

    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. wdrożyć dostawcy ścieżkę wirtualną, która otacza istniejący i przechwycenie wszystkich ścieżek wirtualnych, które powinny dostarczać dynamiczną zawartość.

    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; 
        } 
    } 
    

    Trzeba też wdrożyć CacheDependency jeśli jest to potrzebne:

    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. Zarejestruj dostawcy ścieżki wirtualnej:

    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. Opcjonalnie: Dodaj Intellisense poparcie swoich poglądów. Użyj <script> znaczniki w swoim widokiem i pozwolić im zostać usunięty przez niestandardowym 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); 
        } 
    } 
    

    użyć metody rozszerzenie lub dodać funkcję pomocnika do kontrolera:

    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 
        }; 
    } 
    

kroki są similiar dla innego rodzaju dynamicznej zawartości. Zobacz na przykład Bundling and Minification and Embedded Resources.

Dodano próbne repozytorium do wersji GitHub, jeśli chcesz ją wypróbować.

Powiązane problemy