【问题标题】:VirtualPathProvider in MVC 5MVC 5 中的 VirtualPathProvider
【发布时间】:2014-08-05 21:22:15
【问题描述】:

我似乎无法让自定义 VirtualPathProvider 在 asp.net MVC 5 中工作。

FileExists 方法返回 true,但不会调用 GetFile 方法。 我相信这是因为 IIS 接管了请求并且不让 .NET 处理它。

我已经尝试设置 RAMMFAR 并创建一个自定义处理程序,就像在这个解决方案 https://stackoverflow.com/a/12151501/801189 中一样,但仍然没有运气。我收到错误 404。

我的自定义提供者:

public class DbPathProvider : VirtualPathProvider
{
    public DbPathProvider() : base()
    {

    }

    private static bool IsContentPath(string virtualPath)
    {
        var checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
        return checkPath.StartsWith("~/CMS/", StringComparison.InvariantCultureIgnoreCase);
    }

    public override bool FileExists(string virtualPath)
    {
        return IsContentPath(virtualPath) || base.FileExists(virtualPath);
    }

    public override VirtualFile GetFile(string virtualPath)
    {
        return IsContentPath(virtualPath) ? new DbVirtualFile(virtualPath) : base.GetFile(virtualPath);
    }

    public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
    {
        return null;

    }

    public override String GetFileHash(String virtualPath, IEnumerable virtualPathDependencies)
    {
        return Guid.NewGuid().ToString();
    }
}

我的自定义虚拟文件:

public class DbVirtualFile : VirtualFile
{
    public DbVirtualFile(string path): base(path)
    {

    }

    public override System.IO.Stream Open()
    {
        string testPage = "This is a test!";
        return new System.IO.MemoryStream(System.Text.ASCIIEncoding.ASCII.GetBytes(testPage));
    }
}

web.config 处理程序我尝试使用,但没有成功。它目前给出错误 500:

<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
  <remove name="FormsAuthenticationModule" />
</modules>

<handlers>
  <add name="ApiURIs-ISAPI-Integrated-4.0"
 path="/CMS/*"
 verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
 type="System.Web.Handlers.TransferRequestHandler"
 preCondition="runtimeVersionv4.0" />
</handlers>

如果我尝试导航到 site.com/CMS/Home/Index,则会调用 FileExists 方法,但奇怪的是,virtualPath 参数只接收 ~/CMS/Home。

添加断点,似乎对于 url site.com/CMS/Home/Index,FileExists 方法不断被重复调用。这可能会导致无限递归,从而导致内部服务器错误。

【问题讨论】:

    标签: asp.net-mvc iis asp.net-mvc-routing asp.net-mvc-5


    【解决方案1】:

    这实际上与 IIS 无关,实际上是对事件顺序的混淆。似乎我不明白路由操作方法必须返回一个视图,VirtualPathProvider 将尝试解析,而不是直接转到 VirtualPathProvider。

    我使用单个 GetPage 操作创建了一个名为 ContentPagesController 的简单控制器:

    public class ContentPagesController : Controller
        {
            [HttpGet]
            public ActionResult GetPage(string pageName)
            {
                return View(pageName);
            }
        }
    

    然后我设置路由以提供虚拟页面:

    routes.MapRoute(
     name: "ContentPageRoute",
     url: "CMS/{*pageName}",
     defaults: new { controller = "ContentPages", action = "GetPage" },
     constraints: new { controller = "ContentPages", action = "GetPage" }
    );
    

    在我注册路由之前,我在 globals.asax.cs 中注册了我的自定义 VirtualPathProvider。

    现在假设我的数据库中有一个带有相对 URL /CMS/Home/AboutUs 的页面。 pageName 参数的值为 Home/AboutUs,返回 View() 调用将指示 VirtualPathProvider 查找文件的变体 ~/Views/ContentPages/Home/AboutUs.cshtml。

    它将寻找的一些变化包括:

    ~/Views/ContentPages/Home/AboutUs.aspx
    ~/Views/ContentPages/Home/AboutUs.ascx
    ~/Views/ContentPages/Home/AboutUs.vbhtml
    

    您现在需要做的就是使用数据库查找或类似方法检查传递给 GetFiles 方法的 virtualPath。这是一个简单的方法:

    private bool IsCMSPath(string virtualPath)
            {
               return virtualPath == "/Views/ContentPages/Home/AboutUs.cshtml" || 
                    virtualPath == "~/Views/ContentPages/Home/AboutUs.cshtml"; 
            }
    
            public override bool FileExists(string virtualPath)
            {
                return IsCMSPath(virtualPath) || base.FileExists(virtualPath);
            }
    
            public override VirtualFile GetFile(string virtualPath)
            {
                if (IsCMSPath(virtualPath))
                {
                    return new DbVirtualFile(virtualPath);
                }
    
                return base.GetFile(virtualPath);
            }
    

    将制作自定义虚拟文件并在 GetFile 方法中返回给浏览器。

    最后,可以创建一个自定义视图引擎,以提供发送到 VirtualPathProvider 的不同虚拟视图路径。

    希望这会有所帮助。

    【讨论】:

    • 您的回答很有帮助。我的代码正常工作,但是在“System.IO.Stream Open()”之后视图无法呈现。你遇到过同样的问题吗?
    • 不,我不相信我做到了。您看到了什么错误?
    • 只是在 Open() 之后呈现的空白 html。 Open() 成功从 DB 获取视图
    • 我建议在 Open 方法中只返回一个测试字符串,就像我在最初的问题中所做的那样,看看它是否工作正常。如果是,那么我相信问题出在您的数据库条目上。
    • @DavidJamesBall 谢谢!!在我在这里找到你的帖子之前,我几乎要疯了。我对 /PathToAction.cshtml... 的 /Controller/Action 进行了 FileExists 检查,显然这不起作用。
    猜你喜欢
    • 2014-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-31
    • 2010-09-19
    • 1970-01-01
    • 2011-04-08
    相关资源
    最近更新 更多