Asp.net mvc 4 在渲染视图路径之前,从MVC控制器修改视图路径

Asp.net mvc 4 在渲染视图路径之前,从MVC控制器修改视图路径,asp.net-mvc-4,Asp.net Mvc 4,在我的MVC4控制器中,我想重写View()方法 这样我就可以操纵由action方法呈现的视图。为此,我希望能够获得视图文件的物理路径。我尝试了以下方法: string viewName = this.ControllerContext.RouteData.Route .GetVirtualPath(this.ControllerContext.RequestContext, null) .VirtualPath; 例如,当我真正希望它返回的内容如下时,它可能会返回“/Errors

在我的MVC4控制器中,我想重写View()方法

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

string viewName = this.ControllerContext.RouteData.Route
   .GetVirtualPath(this.ControllerContext.RequestContext, null)
   .VirtualPath;
例如,当我真正希望它返回的内容如下时,它可能会返回“/Errors/MissingParameters”:

"~/Views/Errors/MissingParameters"
"~/Areas/Surveys/Views/Errors/MissingParameters"
或者,更好的是:

"~/Views/Errors/MissingParameters.cshtml"
更复杂的是,我还需要它来处理区域,因此如果我在一个名为“Surveys”的区域中运行相同的示例,我希望它返回如下内容:

"~/Views/Errors/MissingParameters"
"~/Areas/Surveys/Views/Errors/MissingParameters"
我想这样做的原因是我正在尝试使用视图进行全球化,因此我可能有两种视图:

"~/Views/Errors/MissingParameters.cshtml"        // default view (en-GB)
"~/Views/Errors/MissingParameters_de-DE.cshtml"  // German view (de-DE)
我希望在引用它之前能够检查当前语言/文化的视图是否存在

任何建议都将不胜感激

谢谢。

答案是从中复制的

如果您不介意将代码绑定到正在使用的特定视图引擎,您可以查看ViewContext.view属性并将其强制转换为WebFormView

var viewPath=((WebFormView)ViewContext.View).viewPath

我相信这将在最后为您提供视图名称

编辑:哈克德是绝对正确的;为了使事情更整洁,我将逻辑封装在一个扩展方法中,如下所示:

public static class IViewExtensions {
    public static string GetWebFormViewName(this IView view) {
        if (view is WebFormView) {
            string viewUrl = ((WebFormView)view).ViewPath;
            string viewFileName = viewUrl.Substring(viewUrl.LastIndexOf('/'));
            string viewFileNameWithoutExtension = Path.GetFileNameWithoutExtension(viewFileName);
            return (viewFileNameWithoutExtension);
        } else {
            throw (new InvalidOperationException("This view is not a WebFormView"));
        }
    }
}
这似乎正是我想要的

另一个解决方案
((System.Web.Mvc.RazorView)htmlHelper.ViewContext.View).ViewPath

net mvc

答案是从中复制的

如果您不介意将代码绑定到正在使用的特定视图引擎,您可以查看ViewContext.view属性并将其强制转换为WebFormView

var viewPath=((WebFormView)ViewContext.View).viewPath

我相信这将在最后为您提供视图名称

编辑:哈克德是绝对正确的;为了使事情更整洁,我将逻辑封装在一个扩展方法中,如下所示:

public static class IViewExtensions {
    public static string GetWebFormViewName(this IView view) {
        if (view is WebFormView) {
            string viewUrl = ((WebFormView)view).ViewPath;
            string viewFileName = viewUrl.Substring(viewUrl.LastIndexOf('/'));
            string viewFileNameWithoutExtension = Path.GetFileNameWithoutExtension(viewFileName);
            return (viewFileNameWithoutExtension);
        } else {
            throw (new InvalidOperationException("This view is not a WebFormView"));
        }
    }
}
这似乎正是我想要的

另一个解决方案
((System.Web.Mvc.RazorView)htmlHelper.ViewContext.View).ViewPath


net mvc编辑:此部分不起作用或很难实现

您更希望使用一个操作过滤器,它可以让您在执行之前操纵
结果

尤其需要一个结果过滤器。实现该方法,并在那里更改结果。尤其是在实现此方法时:

void OnResultExecuting(ResultExecutingContext filterContext)
您可以访问。此属性将包含您的视图。如果您将其强制转换为,您将有权访问
视图名
,并且可以对其进行更改

如果您从未实现过筛选器,则这是一个。在本例中,它实现了另一种过滤器,但它是一样的

作为对OP注释的回答,
ViewName
缺失,而
View
仍然为空,这是非常正常的<代码>视图名称
只有在返回带有名称的视图时才会为空,如下所示:
返回视图(“索引”)。而且,
ViewName
只是视图的一条路径,而不是视图的整个路径。所以这不是一个解决方案。因此,要使此解决方案起作用,您必须处理路由数据、控制器上下文等以找到视图。(更多信息请参见下文。)

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

当MVC必须呈现视图时,它从路由数据、控制器上下文、视图名称(如上所述,视图名称可以为空)以及应用的约定中获取信息

特别是,在MVC中,有一组注册的视图引擎,需要它们来查找调用那里的
FindView()
方法的视图。视图引擎将返回一个
ViewEngineResult
,其中包含找到的视图(如果找到了),或者返回一个查找视图失败的路径列表

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

要做到这一点,您需要采取以下步骤:

  • 继承您正在使用的视图引擎(我的sampel代码继承Razor视图引擎)
  • 注册您的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);
        }
    
    }
    
    注1:此处阅读
    //此处修改
    您可以修改
    结果的路径属性。查看
    。将其转换为
    RazorView
    (result.View为RazorView)。ViewPath
    。由于
    ViewPath
    setter受到保护,您需要使用反射进行设置:您可以使用
    SetViewPath
    方法进行设置

    注2:如您所见,我不是继承了
    RazorViewEngine
    ,而是继承了
    FixedRazorViewEngine
    。如果在MSDN中查找该类,则不会得到结果,但如果查看“已注册视图引擎”列表的原始内容,则会找到该类。我认为这取决于项目中安装的软件包,我认为它解决了MVC4中的一个bug。如果未在
    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());
    }
    

    注意:如果您在
    App\u Start
    文件夹中创建了一个
    ViewEngineConfig
    类,并调用该类的一个静态方法,就像对所有其他配置所做的那样,那么它会更干净。

    编辑:此部分不起作用或很难实现。

    你会喜欢的
    context.MapRoute(
                    name: "AreaName_default",
                    url: "{controller}/{action}/{id}",
                    namespaces: new[] { "SolutionName.AreaName.Controllers" }
                );
    
    return View("~/Areas/AreaName/Views/ControllerName/ViewName.cshtml", model); 
    
    public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            string areaName = AreaNameAreaRegistration.PropoertyName;
            if (filterContext.Result.GetType() == typeof(ViewResult) || filterContext.Result.GetType() == typeof(PartialViewResult))
            {
                dynamic viewResult = filterContext.Result;
                string viewname = string.IsNullOrEmpty(viewResult.ViewName) ? Convert.ToString(filterContext.RouteData.Values["action"]) : viewResult.ViewName;
                string folder = Convert.ToString(filterContext.RouteData.Values["controller"]);
                string lateralHireAreaViewPath = $"~/Areas/{areaName}/Views/";
                string extension = viewname.Contains(".cshtml") ? "" : ".cshtml";
                viewResult.ViewName = string.Concat(lateralHireAreaViewPath, folder, "/", viewname, extension);
                ViewEngineResult result = ViewEngines.Engines.FindView(filterContext.Controller.ControllerContext, viewResult.ViewName, null);
                if (result.View == null)
                {
                    //searched in shared folder
                    lateralHireAreaViewPath = string.Concat(lateralHireAreaViewPath, "Shared/");
                    viewResult.ViewName = string.Concat(lateralHireAreaViewPath, "/", viewname, extension);
                }
            }
        }