渲染之前从MVC控制器修改视图路径

Nei*_*ski 0 asp.net-mvc-4

在我的MVC 4 Controller中,我想重写View()方法

ViewResult View(string viewName, string masterName, object model) {}
Run Code Online (Sandbox Code Playgroud)

这样我就可以操纵由action方法渲染的视图。为此,我希望能够获取视图文件的物理路径。我尝试了以下方法:

string viewName = this.ControllerContext.RouteData.Route
   .GetVirtualPath(this.ControllerContext.RequestContext, null)
   .VirtualPath;
Run Code Online (Sandbox Code Playgroud)

例如,当我真正希望它返回的内容是这样时,它可能返回“ / Errors / MissingParameters”:

"~/Views/Errors/MissingParameters"
Run Code Online (Sandbox Code Playgroud)

或者,甚至更好:

"~/Views/Errors/MissingParameters.cshtml"
Run Code Online (Sandbox Code Playgroud)

为了增加复杂性,我还需要它来应对Areas,因此,如果我在名为“ Surveys”的Area中运行了相同的示例,则希望它返回类似以下内容的内容:

"~/Areas/Surveys/Views/Errors/MissingParameters"
Run Code Online (Sandbox Code Playgroud)

我要这样做的原因是我正在尝试使用视图进行全球化,因此我可能有两个视图:

"~/Views/Errors/MissingParameters.cshtml"        // default view (en-GB)
"~/Views/Errors/MissingParameters_de-DE.cshtml"  // German view (de-DE)
Run Code Online (Sandbox Code Playgroud)

并且我希望能够在引用该语言/文化之前检查该视图是否存在。

任何建议将不胜感激。

谢谢。

Jot*_*aBe 5

编辑:这部分将无法工作或难以实施

您宁愿使用动作过滤器,该过滤器将让您Result在执行之前进行操作。

特别是您需要一个结果过滤器。实现IResultFilter.onResultExecuting方法,并在那里更改结果。特别是在实现此方法时:

void OnResultExecuting(ResultExecutingContext filterContext)
Run Code Online (Sandbox Code Playgroud)

您可以访问ResultExecutingContext.Result属性。此属性将包含您的视图。如果将其强制转换System.Web.Mvc.ViewResultBase,则可以访问,ViewName并且可以对其进行更改。

如果您从未实现过过滤器,那么这是一个很好的实验主题。在这种情况下,它实现了另一种过滤器,但它是相同的。

作为对OP注释的回答,完全正常的ViewName是缺少它,并且View仍然为null。ViewName不会是空的只有该视图返回与名称,像这样的情况:return View("Index");。而且,那ViewName将是公正的,而不是观点的全部路径。因此,这不是解决方案。因此,要使该解决方案有效,您必须处理路径数据,控制器上下文等才能找到视图。(有关此内容,请参见下文。)

编辑:解决方案,注册自定义视图引擎

当MVC必须渲染视图时,它会从路由数据,控制器上下文,视图名称(如上所述,可以为空)和适用的约定中获取信息。

特别是,在MVC中,有一组注册的视图引擎,它们需要找到调用该FindView()方法的视图。如果找到了视图引擎,则视图引擎将返回ViewEngineResult具有找到的视图的,或未成功找到该视图的路径列表。

因此,要修改模板路径,您可以覆盖此功能:让原始类找到视图,如果找到,则修改路径。

要显示您需要执行以下步骤:

  1. 继承您正在使用的视图引擎(我的示例代码继承了Razor视图引擎)
  2. 注册您的vie引擎,以便在原始视图引擎之前进行查询(在我的示例代码中,我只是清除了已注册的引擎列表,然后注册了我的。原始列表包括razor和Web表单视图引擎)

这是继承的视图引擎的代码:

public class CustomRazorViewEngine : FixedRazorViewEngine
{
    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        ViewEngineResult result
            = base.FindView(controllerContext, viewName, masterName, useCache);
        if (result.View != null)
        {
            // Modify here !!
        }
        return result;
    }

    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
    {
        ViewEngineResult result
            = base.FindPartialView(controllerContext, partialViewName, useCache);
        if (result.View != null)
        {
            // Modify here !!
        }
        return result;
    }

    static readonly PropertyInfo ViewPathProp
        = typeof(RazorView).GetProperty("ViewPath");

    public void SetViewPath(RazorView view, string path)
    {
        ViewPathProp.SetValue(view, path);
    }

}
Run Code Online (Sandbox Code Playgroud)

注意1:在阅读位置,// Modify here !!您可以修改的path属性result.View。投放到RazorView(result.View as RazorView).ViewPath。由于ViewPath设置器受到保护,因此您需要使用Reflection进行设置:您可以SetViewPath为此使用方法。

注意2:如您所见,我不是继承RazorViewEngine而是FixedRazorViewEngine。如果您在MSDN中寻找此类,则不会得到任何结果,但是,如果您查看已注册的视图引擎列表的原始内容,则会找到该类。我认为这取决于项目中已安装的软件包,并且它解决了MVC4中的错误。如果您不在Microsoft.Web.Mvc名称空间中查找它,则继承原始RazorViewEngined

注意3:找到视图后,视图引擎将使用来执行该视图ViewEngineResult,因此,如果您对其进行更改,它将使用新的视图路径来执行

最后,您需要在global.asax应用程序启动事件中更改已注册引擎的列表,如下所示:

protected void Application_Start()
{
    // Original content:
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    // Added content:
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new CustomRazorViewEngine());
}
Run Code Online (Sandbox Code Playgroud)

注意:如果您ViewEngineConfigApp_Start文件夹中创建了一个类,并调用了该类的静态方法,就像使用所有其他配置一样,这样做会更干净。