Using VirtualPathProvider to Host Themes on an Azure CDN

I am trying to implement storage of Azure websites in an Azure CDN.

I copied the files to the CDN, preserving the folder structure, as it was in the original App_Themes folder.

I created VirtualPathProvider and the required Virtualdirectory and VirtualFile classes. The provider is registered in global.asax.

My problem is that the only file that appears to be a CDN is the skin file. All images, css, etc. Still referenced as if they were in the standard App_Themes structure. If I set a breakpoint in my code, then my OpenTheme Open method will be called only for the skin file.

Has anyone been able to implement such a solution?

Any ideas what I'm doing wrong?

[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)] [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.High)] public class RedirectAppThemes : VirtualPathProvider { private bool IsPathVirtual(string virtualPath) { String checkPath = VirtualPathUtility.ToAppRelative(virtualPath); return checkPath.StartsWith("~/App_Themes/", StringComparison.InvariantCultureIgnoreCase); } public override bool FileExists(string virtualPath) { if (IsPathVirtual(virtualPath)) { VirtualThemeFile file = new VirtualThemeFile(virtualPath); return file.Exists; } else { return Previous.FileExists(virtualPath); } } public override VirtualFile GetFile(string virtualPath) { if (IsPathVirtual(virtualPath)) { return new VirtualThemeFile(virtualPath); } else { return Previous.GetFile(virtualPath); } } public override bool DirectoryExists(string virtualDir) { if (IsPathVirtual(virtualDir)) { VirtualThemeDirectory dir = new VirtualThemeDirectory(virtualDir); return dir.Exists; } else { return Previous.DirectoryExists(virtualDir); } } public override VirtualDirectory GetDirectory(string virtualDir) { if (IsPathVirtual(virtualDir)) { return new VirtualThemeDirectory(virtualDir); } else { return Previous.GetDirectory(virtualDir); } } public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) { if (IsPathVirtual(virtualPath)) { return null; } else { return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); } } } [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] public class VirtualThemeDirectory : VirtualDirectory { private string cdnPath = "http://xxxxxxxxx.blob.core.windows.net/"; public VirtualThemeDirectory(string virtualPath) : base(virtualPath) { } public override IEnumerable Children { get { List<object> children = new List<object>(); string dir = this.VirtualPath.Replace("/App_Themes/", String.Empty); CloudBlobClient client = new CloudBlobClient(cdnPath); var blobs = client.ListBlobsWithPrefix(@"webinterfacethemes/" + dir ); foreach (CloudBlobDirectory directory in blobs.OfType<CloudBlobDirectory>()) { VirtualThemeDirectory vtd = new VirtualThemeDirectory(directory.Uri.AbsolutePath.Replace("/webinterfacethemes/", "/App_Themes/")); children.Add(vtd); } foreach (CloudBlob file in blobs.OfType<CloudBlob>()) { VirtualThemeFile vtf = new VirtualThemeFile(file.Uri.AbsolutePath.Replace("/webinterfacethemes/", "/App_Themes/")); children.Add(vtf); } return children; } } public override IEnumerable Directories { get { List<object> children = new List<object>(); string dir = this.VirtualPath.Replace("/App_Themes/", String.Empty); CloudBlobClient client = new CloudBlobClient(cdnPath); var blobs = client.ListBlobsWithPrefix(@"webinterfacethemes/" + dir); foreach (CloudBlobDirectory directory in blobs.OfType<CloudBlobDirectory>()) { VirtualThemeDirectory vtd = new VirtualThemeDirectory(directory.Uri.AbsolutePath.Replace("/webinterfacethemes/", "/App_Themes/")); children.Add(vtd); } return children; } } public override IEnumerable Files { get { List<object> children = new List<object>(); string dir = this.VirtualPath.Replace("/App_Themes/", String.Empty); CloudBlobClient client = new CloudBlobClient(cdnPath); var blobs = client.ListBlobsWithPrefix(@"webinterfacethemes/" + dir); foreach (CloudBlob file in blobs.OfType<CloudBlob>()) { VirtualThemeFile vtf = new VirtualThemeFile(file.Uri.AbsolutePath.Replace("/webinterfacethemes/", "/App_Themes/")); children.Add(vtf); } return children; } } public bool Exists { get { string dir = this.VirtualPath.Replace("/App_Themes/", String.Empty); CloudBlobClient client = new CloudBlobClient(cdnPath); if (client.ListBlobsWithPrefix("webinterfacethemes/" + dir).Count() > 0) return true; else return false; } } } [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] public class VirtualThemeFile : VirtualFile { private string cdnPath = "http://xxxxxxx.vo.msecnd.net/webinterfacethemes/"; public VirtualThemeFile(string VirtualPath) : base(VirtualPath) { } public override Stream Open() { string url = this.VirtualPath.Replace("/App_Themes/", cdnPath); HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(url); WebResponse myResp = myReq.GetResponse(); Stream stream = myResp.GetResponseStream(); return stream; } public bool Exists { get { //Check if the file exists //do this with a HEAD only request so we don't download the whole file string url = this.VirtualPath.Replace("/App_Themes/", cdnPath); HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(url); myReq.Method = "HEAD"; HttpWebResponse myResp = (HttpWebResponse)myReq.GetResponse(); if (myResp.StatusCode == HttpStatusCode.OK) { return true; } else { return false; } } } } 

And in the Global.asax Application_start application:

 RedirectAppThemes redirect = new RedirectAppThemes(); HostingEnvironment.RegisterVirtualPathProvider(redirect); 
+4
source share
1 answer

Mucho Googling eventually found this similar problem

I added the following to my web.config inside the <system.webServer> tags, and now the image and css files call the methods in my code.

 <handlers> <add name="Images" path="*.png" verb="GET,HEAD,POST" type="System.Web.StaticFileHandler" modules="ManagedPipelineHandler" resourceType="Unspecified" /> <add name="Stylesheets" path="*.css" verb="GET,HEAD,POST" type="System.Web.StaticFileHandler" modules="ManagedPipelineHandler" resourceType="Unspecified" /> </handlers> 

If someone is looking for a complete solution to put their themes on CDN using this method, I also changed the following from the source code published above, and now it works:

The VirtualFile class is as follows:

 [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] public class VirtualThemeFile : VirtualFile { private string cdnPath = "https://xxxxxx.vo.msecnd.net/webinterfacethemes/"; private string blobURL; public VirtualThemeFile(string VirtualPath) : base(VirtualPath) { blobURL = this.VirtualPath.Replace("/App_Themes/", cdnPath); } public override Stream Open() { CloudBlobClient client = new CloudBlobClient(cdnPath); CloudBlob blob = client.GetBlobReference(blobURL); MemoryStream stream = new MemoryStream(); blob.DownloadToStream(stream); stream.Seek(0, SeekOrigin.Begin); return stream; } public bool Exists { get { CloudBlobClient client = new CloudBlobClient(cdnPath); CloudBlob blob = client.GetBlobReference(blobURL); try { blob.FetchAttributes(); return true; } catch (StorageClientException e) { if (e.ErrorCode == StorageErrorCode.ResourceNotFound) { return false; } else { throw; } } } } } 

And I changed my global.asax code so that VirtualPathProvider is called if the site is precompiled:

 HostingEnvironment hostingEnvironmentInstance = (HostingEnvironment)typeof(HostingEnvironment).InvokeMember("_theHostingEnvironment", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField, null, null, null); MethodInfo mi = typeof(HostingEnvironment).GetMethod("RegisterVirtualPathProviderInternal", BindingFlags.NonPublic | BindingFlags.Static); mi.Invoke(hostingEnvironmentInstance, new object[] { new RedirectAppThemes() }); 
+3
source

Source: https://habr.com/ru/post/1381662/


All Articles