为什么MVC 5不调用我的VirtualPathProvider类的GetFile方法?

[英]Why MVC 5 does not call GetFile Method of my VirtualPathProvider Class?


I am trying to load Razor View from database.

我正在尝试从数据库加载Razor View。

I follow ASP.NET MVC and virtual views and VirtualPathProvider in MVC 5 to do that.

我在MVC 5中遵循ASP.NET MVC和虚拟视图以及VirtualPathProvider来做到这一点。

my Code :

我的代码:

VirtualPathProvider :

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

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 :

Global.asax:

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

    }

Action :

行动:

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));
    }

Data:

数据:

enter image description here

Case 1:

情况1:

When virtualPath value is "/Views/Home/Contact.cshtml" then FileExists method return true and GetFile method is called.

当virtualPath值为“/Views/Home/Contact.cshtml”时,FileExists方法返回true并调用GetFile方法。

Case 2:

案例2:

When virtualPath value is "~/Home/Display/ce28bbb6-03cb-4bf4-8820-373890396a90" then FileExists method return true and GetFile method and Display Action is never called. and result is

当virtualPath值为“〜/ Home / Display / ce28bbb6-03cb-4bf4-8820-373890396a90”时,FileExists方法返回true,并且永远不会调用GetFile方法和Display Action。结果是

HTTP Error 404.0 - Not Found The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

HTTP错误404.0 - 未找到您要查找的资源已被删除,名称已更改或暂时不可用。

I have no idea about dynamic view. I just read that two article and try to implement it.

我不知道动态视图。我刚看了两篇文章并尝试实现它。

Please tell me where I am doing wrong.

请告诉我我做错了什么。

I am using MVC 5 and .NET 4.5

我正在使用MVC 5和.NET 4.5

3 个解决方案

#1


5  

something I should be able to help you with. There is something you are not thinking about which is ok, because I had loads of trouble with this too.

我应该能够帮助你的东西。有一些你没想到的东西是可以的,因为我也遇到了很多麻烦。

I think the issue you have here is knowing in what order everything fires. This is what happens:

我认为你在这里遇到的问题是知道一切都是以什么顺序发生的。这是发生的事情:

  1. First the VirtualPathProvider FileExists method is called and this is where you are doing your magic to find the page. My methods are slightly different to yours, here is an example:

    首先调用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. Now, if this page is served from the database it is a virtual page, so the next thing that happens is that it calls your MvcHandler (in my handler I have these functions)

    现在,如果这个页面是从数据库提供的,那么它就是一个虚拟页面,所以接下来发生的事情是它调用你的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. Just in case, when you create your MvcHandler if you have not already done so, it must be registered in RouteConfig like this:

    以防万一,如果您尚未创建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. In my MvcHandler I get my page and if it matches any virtual page I move to a RoutingController

    在我的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
        }
    }
    

just for your sake I will show the service.View method

为了你的缘故,我将展示service.View方法

    /// <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. Then we are taken back to our VirtualPathProvider and the process begins again, but this time the virtual path will look something like this: "~/Views/Routing/1331.aspx"
  2. 然后我们回到我们的VirtualPathProvider并再次开始流程,但这次虚拟路径看起来像这样:“〜/ Views / Routing / 1331.aspx”
  3. If you notice, my FindPage method also looks for an extension match which I store in the database too: enter image description here
  4. 如果你注意到,我的FindPage方法也会查找我在数据库中存储的扩展匹配:
  5. Then GetFile will be called and as long as you have followed my path, you should return the VirtualFile
  6. 然后将调用GetFile,只要您遵循我的路径,就应该返回VirtualFile

I really hope that helps :)

我真的希望有帮助:)

#2


3  

I have same issue. Razor view engine not call GetFile if you virtualPath is not *.cshtml format. You need generate {Guid}.cshtml path format

我有同样的问题。如果您的virtualPath不是* .cshtml格式,则Razor视图引擎不会调用GetFile。您需要生成{Guid} .cshtml路径格式

#3


0  

WARNING: ASP.NET 4 (MVC5) relative!

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

Despite the number of answers and a selected one, I will share my experience.

尽管有多少答案和一个选定的答案,我将分享我的经验。

FileExists(string virtualPath)

gets called twice, once with a app relative path ("~/bla-bla") and once more with absolute one ("/bla-bla"). In my case the second one (the abs. path) fails. Seems like in ASP.NET 3 this was OK, however ASP.NET 4 (MVC5) checks it too. And requires it to return true. As a result

被调用两次,一次是app相对路径(“〜/ bla-bla”),再一次是绝对一次(“/ bla-bla”)。在我的情况下,第二个(绝对路径)失败。似乎在ASP.NET 3中这没关系,但ASP.NET 4(MVC5)也检查它。并要求它返回真实。结果是

GetFile(string virtualPath) 

doesn't get called.

没有被召唤。

For those who need to implement such behaviour VirtualPathUtility offers ToAbsolute and ToAppRelative convenience methods.

对于那些需要实现此类行为的人,VirtualPathUtility提供了ToAbsolute和ToAppRelative的便捷方法。

HTH

HTH


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:http://www.itdaan.com/blog/2014/07/19/bb007d3321fb436bddbd7d836702bfde.html



 
© 2014-2018 ITdaan.com 粤ICP备14056181号