Использование VirtualPathProvider для размещения тем в Azure CDN
Я пытаюсь реализовать хранение тем веб-сайта Azure в Azure CDN.
Я скопировал файлы в CDN, сохранив структуру папок, как это было в оригинальной папке App_Themes.
Я создал VirtualPathProvider и необходимые классы Virtualdirectory и VirtualFile. Провайдер зарегистрирован в global.asax.
Моя проблема состоит в том, что единственный файл, который, кажется, прибывает из CDN, является файлом скина. Все изображения, CSS и т. Д. Все еще ссылаются так, как если бы они были в стандартной структуре App_Themes. Если я поставлю точку останова в своем коде, тогда метод Open моей VirtualTheme всегда будет вызываться только для файла скина.
Кому-нибудь удалось реализовать подобное решение?
Есть идеи, что я делаю не так?
[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;
}
}
}
}
И в Global.asax Application_start:
RedirectAppThemes redirect = new RedirectAppThemes();
HostingEnvironment.RegisterVirtualPathProvider(redirect);
1 ответ
Mucho Googling в конце концов обнаружил эту похожую проблему
Я добавил следующее в мой web.config внутри <system.webServer>
теги, а теперь файлы изображений и CSS вызывают методы в моем коде.
<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>
Если кто-то ищет полное решение для размещения своих тем в CDN с использованием этого метода, я также изменил следующий код по сравнению с исходным кодом, опубликованным выше, и теперь он работает:
класс VirtualFile выглядит следующим образом:
[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;
}
}
}
}
}
И я изменил свой код global.asax, чтобы VirtualPathProvider вызывался, если сайт предварительно скомпилирован:
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() });