2014-07-19 75 views
8

我正試圖從數據庫加載Razor視圖。爲什麼MVC 5不調用我的VirtualPathProvider類的GetFile方法?

我按照ASP.NET MVC and virtual viewsVirtualPathProvider in MVC 5來做到這一點。

我的代碼:

的VirtualPathProvider:

public class DbPathProvider : VirtualPathProvider 
    { 
    public override bool FileExists(string virtualPath) 
    { 
     var page = FindPage(virtualPath); 
     if (page == null) 
     { 
      return base.FileExists(virtualPath); 
     } 
     else 
     { 
      return true; 
     } 
    } 

    public override VirtualFile GetFile(string virtualPath) 
    { 
     var page = FindPage(virtualPath); 
     if (page == null) 
     { 
      return base.GetFile(virtualPath); 
     } 
     else 
     { 
      return new DbVirtualFile(virtualPath, page.PageData.ToArray()); 
     } 
    } 

    private SiteModel FindPage(string virtualPath) 
    { 
     var db = new DatabaseContext(); 
     var page = db.SiteModels.FirstOrDefault(x => x.SiteName == virtualPath); 
     return page; 
    } 
} 

VirtualFile

public class DbVirtualFile : VirtualFile 
{ 
    private byte[] data; 

    public DbVirtualFile(string virtualPath, byte[] data) 
     : base(virtualPath) 
    { 
     this.data = data; 
    } 

    public override System.IO.Stream Open() 
    { 
     return new MemoryStream(data); 
    } 
} 

的Global.asax:

protected void Application_Start() 
    { 
     HostingEnvironment.RegisterVirtualPathProvider(new DbPathProvider()); 
     AreaRegistration.RegisterAllAreas(); 
     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
     RouteConfig.RegisterRoutes(RouteTable.Routes); 
     BundleConfig.RegisterBundles(BundleTable.Bundles); 

    } 

行動:

public ActionResult Display(string id) 
    { 
     var db = new DatabaseContext(); 
     var site = db.SiteModels.FirstOrDefault(x => x.PageName == id); 
     if (site == null) 
     { 
      return RedirectToAction("Index", "Home"); 
     } 
     ViewBag.Body = site.PageContent; 
     return View(System.IO.Path.GetFileNameWithoutExtension(site.SiteName)); 
    } 

數據:

enter image description here

情況1:

當virtualPath值爲「/Views/Home/Contact.cshtml」然後FILEEXISTS方法返回的GetFile方法被稱爲

案例2:

當virtualPath值「〜/首頁/顯示/ ce28bbb6-03cb-4bf4-8820-373890396a90」然後FILEEXISTS方法的返回真正GetFile方法和顯示行動從來都不是調用。,結果爲

HTTP錯誤404.0 - 未找到 您正在查找的資源已被刪除,名稱已更改或暫時無法使用。

我不知道動態視圖。我剛剛閱讀了這兩篇文章並試圖實現它。

請告訴我我做錯了什麼。

我使用MVC 5和.NET 4.5

回答

5

東西,我應該能夠幫助你。 有些東西你沒有想到哪些是可以的,因爲我也有很多麻煩。

我認爲你在這裏遇到的問題是知道什麼順序一切都會發生。 這是發生了什麼:

  1. 首先的VirtualPathProvider FILEEXISTS方法被調用, 這是你在做你的魔法找到該網頁。我的方法是,以你的略有不同,這裏有一個例子:

    public IList<Page> Pages 
    { 
        get 
        { 
         using (var uow = new UnitOfWork<SkipstoneContext>()) 
         { 
          var companyService = new CompanyService(uow); 
          var company = companyService.GetTenant(); 
    
          if (company != null) 
          { 
           var pageService = new PageService(uow, company.Id); 
    
           return pageService.GetPublished(); 
          } 
         } 
    
         return null; 
        } 
    } 
    
    public override bool FileExists(string virtualPath) 
    { 
        if (IsVirtualPath(virtualPath)) 
        { 
         if (FindPage(virtualPath) != null) 
         { 
          var file = (PageVirtualFile)GetFile(virtualPath); 
          return file.Exists; 
         } 
        } 
    
        return Previous.FileExists(virtualPath); 
    } 
    
    public override VirtualFile GetFile(string virtualPath) 
    { 
        if (IsVirtualPath(virtualPath)) 
        { 
         var page = FindPage(virtualPath); 
         if (page != null) 
         { 
          var decodedString = Uri.UnescapeDataString(page.ViewData); 
          var bytes = Encoding.ASCII.GetBytes(decodedString); 
    
          return new PageVirtualFile(virtualPath, bytes); 
         } 
        } 
    
        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) 
    { 
        var virtualName = VirtualPathUtility.GetFileName(virtualPath); 
        var virtualExtension = VirtualPathUtility.GetExtension(virtualPath); 
    
        try 
        { 
         if (Pages != null) 
         { 
          var id = Convert.ToInt32(virtualName.Replace(virtualExtension, "")); 
          var page = Pages.Where(model => model.Id == id && model.Extension.Equals(virtualExtension, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); 
    
          return page; 
         } 
        } 
        catch(Exception ex) 
        { 
         // Do nothing 
        } 
    
        return null; 
    } 
    
    private bool IsVirtualPath(string virtualPath) 
    { 
        var path = (VirtualPathUtility.GetDirectory(virtualPath) != "~/") ? VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(virtualPath)) : VirtualPathUtility.GetDirectory(virtualPath); 
        if (path.Equals("~/Views/Routing", StringComparison.OrdinalIgnoreCase) || path.Equals("/Views/Routing", StringComparison.OrdinalIgnoreCase)) 
         return true; 
        else 
         return false; 
    } 
    
  2. 現在,如果這個頁面是從數據庫服務它是一個虛擬的網頁,所以接下來發生的事情是,它要求你的MvcHandler(在我的處理程序我有這些功能)

    protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) 
    { 
        var vpp = new SkipstoneVirtualPathProvider(); // Create an instance of our VirtualPathProvider class 
        var requestContext = ((MvcHandler)httpContext.Handler).RequestContext; // Get our request context 
        var path = requestContext.HttpContext.Request.Url.AbsolutePath; // Get our requested path 
        var pages = vpp.Pages; // Get all the published pages for this company 
    
        if (pages != null && !string.IsNullOrEmpty(path)) // If we have any pages and we have a path 
        { 
         var page = this.MatchPage(pages, path); 
         if (page != null) // If we find the page 
         { 
          requestContext.RouteData.Values["controller"] = "Routing"; // Set the controller 
          requestContext.RouteData.Values["action"] = "Index"; // And the action 
         } 
        } 
    
        return base.BeginProcessRequest(httpContext, callback, state); 
    } 
    
    private Page MatchPage(IList<Page> pages, string path) 
    { 
        if (path == "/" || !path.EndsWith("/")) 
        { 
         var page = pages.Where(model => model.Path.Equals(path, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); // Try to match the path first 
         if (page == null) // If we have no page, then get the directory and see if that matches any page 
         { 
          path = VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(path)); 
          page = pages.Where(model => model.Path.Equals(path, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); 
         } 
    
         return page; // return our page or null if no page exists 
        } 
    
        return null; // return null if anything fails 
    } 
    
  3. 以防萬一,當你創建MvcHandler如果你還沒有這樣做的話,就必須在註冊RouteConfig這樣的:

    // default MVC route 
        routes.MapRoute(
         name: "Default", 
         url: "{controller}/{action}/{id}", 
         defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
        ).RouteHandler = new SkipstoneRouteHandler(); 
    
  4. 在我MvcHandler我得到我的網頁,如果它匹配任何虛擬頁我移動到RoutingController

    public ActionResult Index() 
    { 
        using (var uow = new UnitOfWork<SkipstoneContext>()) 
        { 
         var userId = User.Identity.GetUserId(); 
         var service = new PageService(uow, this.CompanyId); 
         var userService = new UserService(uow, this.CompanyId); 
         var user = userService.Get(userId); 
    
         var fileName = service.View(Request.Url, user); 
    
         if (!fileName.StartsWith(@"/") && !fileName.StartsWith("~/")) 
          return View(fileName); // Show the page 
         else 
          return Redirect(fileName); // Redirect to our page 
        } 
    } 
    

只爲你的緣故,我將顯示服務。查看方法

/// <summary> 
    /// Views the CMS page 
    /// </summary> 
    /// <param name="uri">The current Request.Url</param> 
    /// <param name="user">The current User</param> 
    /// <returns>The filename of the requested page</returns> 
    public string View(Uri uri, User user) 
    { 
     var path = uri.AbsolutePath; // Get our Requested Url 
     var queryString = uri.Query; 
     var pages = this.GetPublished(); 
     var page = pages.Where(model => model.Path.Equals(path, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); // Try to get the page 

     if (page == null) // If our page is null 
      page = pages.Where(model => model.Path.Equals(VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(path)))).SingleOrDefault(); // try to get the page based off the directory 

     if (page == null) // If the page is still null, then it doesn't exist 
      throw new HttpException(404, "This page has been deleted or removed."); // Throw the 404 error 

     if (page.Restricted && user == null) // If our page is restricted and we are not logged in 
      return "~/Account/LogOn?ReturnUrl=" + page.Path + queryString; // Redirect to the login page 

     if (user != null && page.Restricted) 
     { 
      if (PageService.IsForbidden(page, user)) 
       throw new HttpException(401, "You do not have permission to view this page."); // Throw 401 error 
     } 

     return Path.GetFileNameWithoutExtension(page.Id.ToString()); 
    } 
  1. 然後我們會回到我們的的VirtualPathProvider和過程又重新開始,但這次的虛擬路徑將是這個樣子:「〜/查看/路由/ 1331.aspx」
  2. 如果您通知,我查找網頁方法看起來也爲此我在數據庫中存儲過延期比賽:enter image description here
  3. 然後的GetFile將被調用,只要你按照我的路,你應該返回VirtualFile

我真的希望幫助:)

+0

能不能介紹一下爲什麼GetCacheDependency返回null評論,爲什麼GetFileHash返回新Guid.NewGuid()? –

+0

當然,這是因爲我禁用從數據庫中提取的項目緩存 – r3plica

+1

謝謝!在這裏省了很多時間......在我的情況下,還需要在路由配置中添加'routes.RouteExistingFiles = true;',否則'404找不到'。 –

3

我有同樣的問題。 如果您的virtualPath不是* .cshtml格式,Razor視圖引擎不會調用GetFile。 您需要生成{Guid} .cshtml路徑格式

+0

,情況不應該如此。它實際上會爲所有擴展類型啓動。 – r3plica

+0

我同意,它不應該是這樣,但這是我看到的同樣的行爲。 –

0

警告:ASP.NET 4(MVC5)相對!

儘管有多個答案和一個選定的答案,我會分享我的經驗。

FileExists(string virtualPath) 

被調用兩次,一旦與一個應用程序相對路徑( 「〜/ BLA-BLA」)和再次用一個絕對( 「/ BLA-BLA」)。在我的情況下,第二個(絕對路徑)失敗。看起來像在ASP.NET 3中一樣,但是ASP.NET 4(MVC5)也檢查它。並要求它返回true。因此,

GetFile(string virtualPath) 

不會被調用。

對於需要實施此類行爲的人VirtualPathUtility優惠ToAbsoluteToAppRelative便利方法。

HTH

相關問題