4

What is the proper way to get the physical location of the View that will be served by a MVC action from inside the action?

I need the last modified time of the file for sending response headers.

Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
  • Note, this question does not dupe http://stackoverflow.com/questions/4135450/determine-physical-location-of-razor-view-in-mvc3 that question is about being inside the view and not inside the controller. – Chris Marisic Apr 12 '12 at 17:29
  • 1
    I may be wrong but that wont be avalaible until post-action execute, or OnActionExecuted. I suppose you could loop through all of the registered ViewEngines and try finding the views based on the ViewLocation collection, but thats all manual – Nick Bork Apr 12 '12 at 17:35
  • Also does not dupe http://stackoverflow.com/questions/1529417/asp-net-mvc-getting-last-modified-date-fileinfo-of-view this question might be for MVC1, the Request.PhysicalPath returns an invalid file location, it just treats my route as if it was a file request. – Chris Marisic Apr 12 '12 at 17:59

2 Answers2

4

The proper way to the get physical location of a view is to map its virtual path. The virtual path can be retrieved from the ViewPath property of BuildManagerCompiledView (RazorView derive from that class, and your IView instances will therefore typically have that property).

Here is an extension method that you can use:

public static class PhysicalViewPathExtension
{
    public static string GetPhysicalViewPath(this ControllerBase controller, string viewName = null)
    {
        if (controller == null)
        {
            throw new ArgumentNullException("controller");
        }

        ControllerContext context = controller.ControllerContext;

        if (string.IsNullOrEmpty(viewName))
        {
            viewName = context.RouteData.GetRequiredString("action");
        }

        var result = ViewEngines.Engines.FindView(context, viewName, null);
        BuildManagerCompiledView compiledView = result.View as BuildManagerCompiledView;

        if (compiledView != null)
        {
            string virtualPath = compiledView.ViewPath;
            return context.HttpContext.Server.MapPath(virtualPath);
        }
        else
        {
            return null;
        }
    }
}

Use it something like this:

public ActionResult Index()
{
    string physicalPath = this.GetPhysicalViewPath();
    ViewData["PhysicalPath"] = physicalPath;
    return View();
}

or:

public ActionResult MyAction()
{
    string physicalPath = this.GetPhysicalViewPath("MyView");
    ViewData["PhysicalPath"] = physicalPath;
    return View("MyView");
}
Mårten Wikström
  • 11,074
  • 5
  • 47
  • 87
0

That could work:

private DateTime? GetDate(string controller, string viewName)
{
    var context = new ControllerContext(Request.RequestContext, this);
    context.RouteData.Values["controller"] = controller;
    var view = ViewEngines.Engines.FindView(context, viewName, null).View as BuildManagerCompiledView;
    var path = view == null ? null : view.ViewPath;
    return path == null ? (DateTime?) null : System.IO.File.GetLastWriteTime(path);
}
Dmitry Pavlov
  • 30,789
  • 8
  • 97
  • 121