Я пытаюсь создать веб-сайт MVC с некоторыми представлениями, обслуживаемыми из базы данных. Я начал реализовывать всю логику, используя VirtualFiles и VirtualPathProviders, и все отлично работает на IISExpress... проблема возникает, когда я переключаюсь на локальный IIS. Виртуальный файл найден, но ничего не происходит, я получаю ошибку 404. Я предполагаю, что это IIS, ожидающий, что файл физически существует, но я понятия не имею, как заставить его работать, если это не так.
Я использую следующий пример для тестирования:
DynamicPages.cs
public class Page
{
public string Name { get; set; }
public string UrlName { get; set; }
public string View { get; set; }
}
public class PageService
{
private List<Page> pages;
public PageService()
{
pages = new List<Page>();
pages.Add(new Page() { Name = "test2", UrlName = "test2.cshtml", View = "test2" });
pages.Add(new Page() { Name = "test3", UrlName = "test3.cshtml", View = "test3" });
}
public List<Page> GetAllPages()
{
return pages;
}
}
public class PageVirtualFile : VirtualFile
{
private string data;
public PageVirtualFile(string virtualPath, string data)
: base(virtualPath)
{
this.data = data;
}
public bool Exists { get { return true; } }
public override System.IO.Stream Open()
{
byte[] byteArray = Encoding.UTF8.GetBytes(data);
MemoryStream stream = new MemoryStream(byteArray);
return stream;
}
}
public class CmsRouteHandler : IRouteHandler
{
public class CmsVirtualPathProvider : VirtualPathProvider
{
public IEnumerable<Page> pages
{
get
{
return (new PageService()).GetAllPages();
}
}
public override bool FileExists(string virtualPath)
{
if (IsVirtualPath(virtualPath))
{
if (FindPage(virtualPath) != null)
{
PageVirtualFile file = (PageVirtualFile)GetFile(virtualPath);
return file.Exists;
}
}
return Previous.FileExists(virtualPath);
}
public override VirtualFile GetFile(string virtualPath)
{
if (IsVirtualPath(virtualPath))
{
Page oPage = FindPage(virtualPath);
if (oPage != null)
return new PageVirtualFile(virtualPath, oPage.View);
}
return Previous.GetFile(virtualPath);
}
public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
{
if (IsVirtualPath(virtualPath))
return null;
return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
}
public override string GetFileHash(string virtualPath, System.Collections.IEnumerable virtualPathDependencies)
{
if (IsVirtualPath(virtualPath))
return Guid.NewGuid().ToString();
return Previous.GetFileHash(virtualPath, virtualPathDependencies);
}
private Page FindPage(string virtualPath)
{
string VirtualName = VirtualPathUtility.GetFileName(virtualPath).ToLower();
if (pages != null)
{
Page oPage = pages.SingleOrDefault(page => string.Equals(page.UrlName, VirtualName, StringComparison.InvariantCultureIgnoreCase));
return oPage;
}
else
return null;
}
private bool IsVirtualPath(string virtualPath)
{
string Path = (VirtualPathUtility.GetDirectory(virtualPath) != "~/") ? VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(virtualPath)) : VirtualPathUtility.GetDirectory(virtualPath);
if (Path.StartsWith("/Views/_Cms", StringComparison.InvariantCultureIgnoreCase) || Path.StartsWith("~/Views/_Cms", StringComparison.InvariantCultureIgnoreCase))
return true;
else
return false;
}
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new CmsHandler(requestContext);
}
class CmsHandler : MvcHandler
{
public CmsHandler(RequestContext requestContext)
: base(requestContext)
{
}
protected override IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)
{
return base.BeginProcessRequest(httpContext, callback, state);
}
protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
{
try
{
var vpp = new CmsVirtualPathProvider();
var requestContext = ((MvcHandler)httpContext.Handler).RequestContext;
string path = requestContext.HttpContext.Request.Url.AbsolutePath;
if (path != null)
{
IEnumerable<Page> pages = vpp.pages;
if (pages != null)
{
Page oPage = pages.SingleOrDefault(page => string.Equals((requestContext.HttpContext.Request.ApplicationPath + "/").Replace("//","/") + page.Name, path, StringComparison.InvariantCultureIgnoreCase)); // Select the page matching our requested path (if any)
if (oPage != null)
{
requestContext.RouteData.Values["controller"] = "_Cms";
requestContext.RouteData.Values["action"] = "Index";
}
}
}
return base.BeginProcessRequest(httpContext, callback, state);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
protected override void ProcessRequest(HttpContext httpContext)
{
base.ProcessRequest(httpContext);
}
}
}
Затем я регистрирую свой VirtualPathProvider в Global.asax.
void Application_Start(object sender, EventArgs e)
{
HostingEnvironment.RegisterVirtualPathProvider(new CmsHandlerSandBox.CmsRouteHandler.CmsVirtualPathProvider());
}
Если я использую IISExpress, будет работать следующая ссылка:
http://localhost:<port>/test2
Однако в локальном IIS он изменится на:
http://localhost/<websitename>/test2
и это выдаст ошибку 404.
Любые идеи, как это решить. Я нахожу много более или менее похожих случаев в Google, но ни один из них не работает.
Спасибо заранее!