How to Load an HTML View Other Than With an MVC View File?

Have you ever asked yourself if it was possible to display some HTML content in an ASP.NET MVC application, using another way than through a traditional MVC view? This article will cover that exactly.

The principle is to short circuit the views resolution mechanism.

Step 1 : Override the "VirtualPathProvider" class

We'll override "FileExists" and "GetFile" methods and then create filters that will detect the views to short circuit.

In this example, I have created a private member "filters" that defines the list of the views we want to "catch" and load them using another way.

Then, we override "FileExists" and "GetFile" methods to catch our filters.

It's necessary to simulate a "real" path for our views we want to catch, that's what "PathFormatter" will do.

Finally, "GetViewBytesData" will load the short circuited content and send it in to another overrided class : "CustomVirtualFile".

You can implement this method as you like, and load different views from anywhere (flat file, webservice, database...)

public class CustomVirtualPathProvider : VirtualPathProvider
    {
        private static List<string> filters = new List<string>()
        {
            PathFormatter("PhantomView")
        };
               
        public override bool FileExists(string virtualPath)
        {
            if (!filters.Contains(virtualPath))
                return base.FileExists(virtualPath);
            return true;
        }
      
        public override VirtualFile GetFile(string virtualPath)
        {
            if (!filters.Contains(virtualPath))
                return base.GetFile(virtualPath);
            return new CustomVirtualFile(virtualPath, GetViewBytesData());
        }
       
        private byte[] GetViewBytesData()
        {
            string htmlBody = @"@{    Layout = null;}
                        <hgroup>    
                            <h1>@ViewBag.Title.</h1>
                            <h2>@ViewBag.Message</h2>
 
                            <div>Message:      @Model.Message         </div>
                        </hgroup>  ";
            return Encoding.ASCII.GetBytes(htmlBody);
        }

        public static string PathFormatter(string viewName)
        {
            return string.Format(@"/Views/Shared/{0}.cshtml",viewName);
        }
    }

Step 2 : Implement a custom VirtualFile class derived from the abstract class "VirtualFile"

By implementing "Open" methods, this class allows you to inject content from anywhere instead of the classical MVC view approach: 

public class CustomVirtualFile : VirtualFile
    {
        private readonly byte[] _data;

        public CustomVirtualFile(string virtualPath, byte[] data)
            : base(virtualPath)
        {
            _data = data;
        }

        public override Stream Open()
        {
            return new MemoryStream(_data);
        }
    }

Step 3 : Register our own VirtualPathProvider into the Global.asax file

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            HostingEnvironment.RegisterVirtualPathProvider(new CustomVirtualPathProvider());
        }

Step 4 : Create the folder "Shared" if it doesn't exist (by default, it should becreated automatically when you create a new MVC project)

Step 5 : Create a controller and an action that render a "fake" view

   public class PhantomController : Controller
    {
        public ActionResult Index()
        {
            ViewData["Title"] = "The phantom view";
            ViewBag.Message = "Welcome into the phantom view!!!!";

            var model = new PhantomModel
            {
                Message = "Ouuuuh I don't exist!"
            };

            return View(CustomVirtualPathProvider.PathFormatter("PhantomView"), model);
        }
    }

As you can see, I'm using a classical model, ViewBag and and the ViewData.

Remember, the content of our "fake" view is defined in the "GetViewBytesData" on step 1

Step 6 : Test it!

phantom.png

Funny isn't it? :)