1

I have a working MVC site. To increase its visual appeal, I would like to recreate all the views to use Bootstrap. This will not require any changes to the models or controllers.

During the transition period, I would like users to be directed to different views depending on whether or not a particular "Beta mode" cookie is set. For example, they would be directed to one of:

Index.cshtml
Index.beta.cshtml

How can this be done, without having to change the code in every controller?

James
  • 7,343
  • 9
  • 46
  • 82
  • 1
    Have you looked at overriding the ViewEngine? http://stackoverflow.com/questions/20071809/overriding-mvc-4-0-viewengine or http://stackoverflow.com/questions/13842638/mvc4-razor-custom-view-locator or http://blog.thekfactor.info/posts/asp-net-mvc-custom-view-engines-using-the-razor-view-engine-as-the-base/ – Jack Apr 06 '14 at 19:29

2 Answers2

1

Following the posts I've linked, I've made a naive implementation. Note: This is untested tested. For my testing I hardcoded IsBeta to return true, then it could not find any views (because I have no views named ViewName.beta.cshtml). After renaming Index.cshtml to Index.beta.cshtml it resolved the Index view as expected.

public class BetaViewEngineTest : RazorViewEngine
    {
        public BetaViewEngineTest() : base()
        {
            AreaViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}%1.cshtml",
                             "~/Areas/{2}/Views/{1}/{0}%1.vbhtml",
                             "~/Areas/{2}/Views/Shared/{0}%1.cshtml",
                             "~/Areas/{2}/Views/Shared/{0}%1.vbhtml" };
            AreaMasterLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}%1.cshtml",
                               "~/Areas/{2}/Views/{1}/{0}%1.vbhtml",
                               "~/Areas/{2}/Views/Shared/{0}%1.cshtml",
                               "~/Areas/{2}/Views/Shared/{0}%1.vbhtml" };
            AreaPartialViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}%1.cshtml",
                                "~/Areas/{2}/Views/{1}/{0}%1.vbhtml",
                                "~/Areas/{2}/Views/Shared/{0}%1.cshtml",
                                "~/Areas/{2}/Views/Shared/{0}%1.vbhtml" };
            ViewLocationFormats = new string[] { "~/Views/{1}/{0}%1.cshtml",
                         "~/Views/{1}/{0}%1.vbhtml",
                         "~/Views/Shared/{0}%1.cshtml",
                         "~/Views/Shared/{0}%1.vbhtml" };
            MasterLocationFormats = new string[] { "~/Views/{1}/{0}%1.cshtml",
                           "~/Views/{1}/{0}%1.vbhtml",
                           "~/Views/Shared/{0}%1.cshtml",
                           "~/Views/Shared/{0}%1.vbhtml" };
            PartialViewLocationFormats = new string[] { "~/Views/{1}/{0}%1.cshtml",
                            "~/Views/{1}/{0}%1.vbhtml",
                            "~/Views/Shared/{0}%1.cshtml",
                            "~/Views/Shared/{0}%1.vbhtml" };
            FileExtensions = new string[] { "cshtml", "vbhtml" };

        }

        protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
        {
            return base.CreateView(controllerContext, IsBeta(controllerContext) ? viewPath.Replace("%1", ".beta") : viewPath.Replace("%1", ""), masterPath);
        }

        protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
        {
            return base.CreatePartialView(controllerContext, IsBeta(controllerContext) ? partialPath.Replace("%1", ".beta") : partialPath.Replace("%1", ""));
        }

        protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
        {
            var isBeta = IsBeta(controllerContext);
            if (isBeta)
            {
                return base.FileExists(controllerContext, virtualPath.Replace("%1", ".beta"));
            }
            else
            {
                return base.FileExists(controllerContext, virtualPath);
            }
        }

        private bool IsBeta(ControllerContext ctx) 
        {
            bool isBeta = false;

            var httpCookie = ctx.HttpContext.Request.Cookies["Beta"];

            if (httpCookie != null)
                Boolean.TryParse(httpCookie.Value, out isBeta);

            return isBeta;
        }

    }

Almost forgot, Global.asax.cs, add these lines:

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new BetaViewEngineTest());
Jack
  • 9,156
  • 4
  • 50
  • 75
1

Similar answer to Jack..

public class BetaViewEngine : RazorViewEngine
{
    private readonly IBetaCookieChecker _betaCookieChecker;

    public ToolsViewEngine(IBetaCookieChecker betaCookieChecker)
    {
        _betaCookieChecker = betaCookieChecker;
    }

    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        if (_betaCookieChecker.UserHasBetaCookie())
        {
            ViewLocationFormats = new[]
                {
                    "~/Views/{1}/{0}.beta.cshtml",
                };
            PartialViewLocationFormats = new[]
                {
                    "~/Views/{1}/{0}.beta.cshtml",
                    "~/Views/Shared/{0}.beta.cshtml",
                };                
        }

        return base.FindView(controllerContext, viewName, masterName, useCache);
    }
}

IBetaCookieChecker will have similar impl to Jack's IsBeta method.

You will also need to inject IBetaCookieChecker in global.asax perhaps like ViewEngines.Engines.Add(new ToolsViewEngine(container.Resolve<IBetaCookieChecker>()));

Matt Kocaj
  • 11,278
  • 6
  • 51
  • 79