Asp.net-Mvc

MVC 5 中的 VirtualPathProvider

  • August 5, 2019

我似乎無法讓自定義 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 方法不斷被重複呼叫。這可能會導致無限遞歸,從而導致內部伺服器錯誤。

它實際上與 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 的不同虛擬視圖路徑。

希望這可以幫助。

引用自:https://stackoverflow.com/questions/24237123