自定义未找到的路线仅触发一次

时间:2011-09-18 14:58:54

标签: asp.net-mvc-3 routing httphandler

我倾向于不喜欢发布数十行代码,并假设整个社区都有兴趣解开我的烂摊子。在这种情况下,我已经运用了我能想到的一切来搜索Google,通过Glimpse和Firebug / Fiddler进行搜索,我剩下的是偶尔的工作行为,这对调试来说特别烦人。所以,我正在呼救。

这是要点:由于@AndrewDavey,我有一系列处理MVC路由的类,否则将找不到(并会产生404错误)。我试图拦截404并显示任何存在的数据驱动内容。这一切都有效,直到我刷新页面。该请求适用于第一次加载,但在此之后它永远不会再次触发。

如果您感到无聊或有痒,整个代码块都在下面。

设置如下:

  • 通过NuGet添加WebActivator
  • 在AppStart文件夹中添加一个cs文件,其中包含以下代码
  • 将“PageContext”连接字符串添加到web.config
  • 运行应用程序,默认MVC屏幕显示
  • 现在将“/ abc”添加到网址的末尾(即http://localhost/abc
  • 存储在数据库中的cshtml视图将呈现。
  • 更改数据库中的视图标记并重新加载页面。 请注意您的浏览器没有变化。

/ abc route假设您在数据库中有以下记录

  • 路径:“〜/ abc / index.cshtml”

  • 查看:"@{ Layout = null;}<!doctype html><html><head><title>abc</title></head><body><h2>About</h2></body></html>"

我不知道为什么第一个请求有效,后续请求没有达到断点并提供过时的内容。

我怀疑是:

  • 使用VirtualFile的一些伏都教
  • 缓存的东西(但在哪里?)
  • 配置错误的处理程序

感谢您的帮助 - 这是代码(因为我羞辱地发布了这么多代码)。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using SomeCms;

[assembly: WebActivator.PreApplicationStartMethod(typeof(Sample.Web.App_Start.cms), "PreStart")]

namespace Sample.Web.App_Start
{
    public static class cms
    {
        public static void PreStart()
        {
            DynamicModuleUtility.RegisterModule(typeof(InstallerModule));
        }
    }
}

namespace SomeCms
{
    class ActionInvokerWrapper : IActionInvoker
    {
        readonly IActionInvoker actionInvoker;

        public ActionInvokerWrapper(IActionInvoker actionInvoker)
        {
            this.actionInvoker = actionInvoker;
        }

        public bool InvokeAction(ControllerContext controllerContext, string actionName)
        {
            if (actionInvoker.InvokeAction(controllerContext, actionName))
            {
                return true;
            }

            // No action method was found.
            var controller = new CmsContentController();
            controller.ExecuteCmsContent(controllerContext.RequestContext);

            return true;
        }
    }

    class ControllerFactoryWrapper : IControllerFactory
    {
        readonly IControllerFactory factory;

        public ControllerFactoryWrapper(IControllerFactory factory)
        {
            this.factory = factory;
        }

        public IController CreateController(RequestContext requestContext, string controllerName)
        {
            try
            {
                var controller = factory.CreateController(requestContext, controllerName);
                WrapControllerActionInvoker(controller);
                return controller;
            }
            catch (HttpException ex)
            {
                if (ex.GetHttpCode() == 404)
                {
                    return new CmsContentController();
                }

                throw;
            }
        }

        static void WrapControllerActionInvoker(IController controller)
        {
            var controllerWithInvoker = controller as Controller;
            if (controllerWithInvoker != null)
            {
                controllerWithInvoker.ActionInvoker = new ActionInvokerWrapper(controllerWithInvoker.ActionInvoker);
            }
        }

        public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
        {
            return factory.GetControllerSessionBehavior(requestContext, controllerName);
        }

        public void ReleaseController(IController controller)
        {
            factory.ReleaseController(controller);
        }
    }

    class InstallerModule : IHttpModule
    {
        static bool installed;
        static readonly object installerLock = new object();

        public void Init(HttpApplication application)
        {
            if (installed)
            {
                return;
            }

            lock (installerLock)
            {
                if (installed)
                {
                    return;
                }

                Install();
                installed = true;
            }
        }

        static void Install()
        {
            Database.SetInitializer(new CreateDatabaseIfNotExists<PageContext>());
            HostingEnvironment.RegisterVirtualPathProvider(new ExampleVirtualPathProvider());
            WrapControllerBuilder();
            AddNotFoundRoute();
            AddCatchAllRoute();
        }

        static void WrapControllerBuilder()
        {
            ControllerBuilder.Current.SetControllerFactory(new ControllerFactoryWrapper(ControllerBuilder.Current.GetControllerFactory()));
        }

        static void AddNotFoundRoute()
        {
            // To allow IIS to execute "/cmscontent" when requesting something which is disallowed,
            // such as /bin or /add_data.
            RouteTable.Routes.MapRoute(
                "CmsContent",
                "cmscontent",
                new { controller = "CmsContent", action = "CmsContent" }
            );
        }

        static void AddCatchAllRoute()
        {
            RouteTable.Routes.MapRoute(
                "CmsContent-Catch-All",
                "{*any}",
                new { controller = "CmsContent", action = "CmsContent" }
            );
        }

        public void Dispose() { }
    }

    public class CmsContentController : IController
    {
        public void Execute(RequestContext requestContext)
        {
            ExecuteCmsContent(requestContext);
        }

        public void ExecuteCmsContent(RequestContext requestContext)
        {
            //new CmsContentViewResult().ExecuteResult(new ControllerContext(requestContext, new FakeController()));
            new CmsContentViewResult().ExecuteResult(new ControllerContext(requestContext, new FakeController()));
        }

        // ControllerContext requires an object that derives from ControllerBase.
        // NotFoundController does not do this.
        // So the easiest workaround is this FakeController.
        class FakeController : Controller { }
    }

    public class CmsContentHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            var routeData = new RouteData();
            routeData.Values.Add("controller", "CmsContent");
            var controllerContext = new ControllerContext(new HttpContextWrapper(context), routeData, new FakeController());
            var cmsContentViewResult = new CmsContentViewResult();
            cmsContentViewResult.ExecuteResult(controllerContext);
        }

        public bool IsReusable
        {
            get { return false; }
        }

        // ControllerContext requires an object that derives from ControllerBase.
        class FakeController : Controller { }
    }

    public class CmsContentViewResult : ViewResult
    {
        public CmsContentViewResult()
        {
            ViewName = "index";
        }

        public override void ExecuteResult(ControllerContext context)
        {
            var request = context.HttpContext.Request;
            if (request != null && request.Url != null)
            {
                var url = request.Url.OriginalString;

                ViewData["RequestedUrl"] = url;
                ViewData["ReferrerUrl"] = (request.UrlReferrer != null && request.UrlReferrer.OriginalString != url)
                                              ? request.UrlReferrer.OriginalString
                                              : null;
            }

            base.ExecuteResult(context);
        }
    }

    public class ExampleVirtualPathProvider : VirtualPathProvider
    {
        private readonly List<SimpleVirtualFile> virtualFiles = new List<SimpleVirtualFile>();

        public ExampleVirtualPathProvider()
        {
            var context = new PageContext();
            var pages = context.Pages.ToList();

            foreach (var page in pages)
            {
                virtualFiles.Add(new SimpleVirtualFile(page.Path));

            }
        }

        public override bool FileExists(string virtualPath)
        {
            var files = (from f in virtualFiles
                         where f.VirtualPath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase) ||
                               f.RelativePath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase)
                         select f)
                         .ToList();

            return files.Count > 0 || base.FileExists(virtualPath);
        }

        private class SimpleVirtualFile : VirtualFile
        {
            public SimpleVirtualFile(string filename) : base(filename)
            {
                RelativePath = filename;
            }

            public override Stream Open()
            {
                var context = new PageContext();
                var page = context.Pages.FirstOrDefault(p => p.Path == RelativePath);

                return new MemoryStream(Encoding.ASCII.GetBytes(page.View), false);
            }

            public string RelativePath { get; private set; }
        }

        private class SimpleVirtualDirectory : VirtualDirectory
        {
            public SimpleVirtualDirectory(string virtualPath)
                : base(virtualPath)
            {

            }

            public override IEnumerable Directories
            {
                get { return null; }
            }

            public override IEnumerable Files
            {
                get
                {
                    return null;
                }
            }

            public override IEnumerable Children
            {
                get { return null; }
            }
        }

        public override VirtualFile GetFile(string virtualPath)
        {
            var files = (from f in virtualFiles
                         where f.VirtualPath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase) ||
                               f.RelativePath.Equals(virtualPath, StringComparison.InvariantCultureIgnoreCase)
                         select f).ToList();
            return files.Count > 0
                ? files[0]
                : base.GetFile(virtualPath);
        }

        public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
        {
            return IsPathVirtual(virtualPath) ? null : base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
        }

        private bool IsPathVirtual(string virtualPath)
        {
            var checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
            return
                virtualFiles.Any(f => checkPath.StartsWith(virtualPath, StringComparison.InvariantCultureIgnoreCase)) ||
                virtualFiles.Any(f => checkPath.Replace("~", "").StartsWith(virtualPath, StringComparison.InvariantCultureIgnoreCase));
        }

        public override bool DirectoryExists(string virtualDir)
        {
            return IsPathVirtual(virtualDir) || Previous.DirectoryExists(virtualDir);
        }

        public override VirtualDirectory GetDirectory(string virtualDir)
        {
            return IsPathVirtual(virtualDir)
                ? new SimpleVirtualDirectory(virtualDir)
                : Previous.GetDirectory(virtualDir);
        }
    }

    public class ContentPage
    {
        public int Id { get; set; }
        public string Path { get; set; }
        public string View { get; set; }
    }

    public class PageContext : DbContext
    {
        public DbSet<ContentPage> Pages { get; set; }
    }
}

1 个答案:

答案 0 :(得分:2)

这个问题结果证明不是问题。我对虚拟路径提供程序中的缓存依赖性的监督是为虚拟路径返回null。因此,视图无限期缓存。

解决方案是使用立即过期的自定义缓存依赖项提供程序。

public class NoCacheDependency : CacheDependency
{
    public NoCacheDependency()
    {
        NotifyDependencyChanged(this, EventArgs.Empty);
    }
}

public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
{
     return IsPathVirtual(virtualPath) ? new NoCacheDependency() : base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
}