在我的MVC 4 Controller中,我想覆盖View()方法
ViewResult View(string viewName, string masterName, object model) {}
这样我就可以操作action方法呈现的视图了。为此,我希望能够获取视图文件的物理路径。我尝试过以下方法:
string viewName = this.ControllerContext.RouteData.Route
.GetVirtualPath(this.ControllerContext.RequestContext, null)
.VirtualPath;
例如,当我真正希望它返回时,这可能会返回“/ Errors / MissingParameters”:
"~/Views/Errors/MissingParameters"
或者,甚至更好:
"~/Views/Errors/MissingParameters.cshtml"
只是为了添加复杂功能,我还需要它来处理区域,所以如果我在名为“Surveys”的区域中运行相同的示例,我希望它返回类似的内容:
"~/Areas/Surveys/Views/Errors/MissingParameters"
我想这样做的原因是我正在尝试使用全局视图,所以我可能有两种观点:
"~/Views/Errors/MissingParameters.cshtml" // default view (en-GB)
"~/Views/Errors/MissingParameters_de-DE.cshtml" // German view (de-DE)
我希望能够在引用它之前检查当前语言/文化的视图是否存在。
非常感谢任何建议。
感谢。
答案 0 :(得分:5)
编辑:此部分无效或难以实施
您宁可使用动作过滤器,以便在执行前操作Result
。
特别需要结果过滤器。实现IResultFilter.onResultExecuting方法,并在那里更改结果。特别是在实现此方法时:
void OnResultExecuting(ResultExecutingContext filterContext)
您可以访问ResultExecutingContext.Result Property。此属性将包含您的视图。如果您将其投放到System.Web.Mvc.ViewResultBase,则可以访问ViewName
,然后您就可以更改它。
如果您从未实施过滤器,则为good hands-on-lab on the subject。在这种情况下,它实现了另一种过滤器,但它是一样的。
作为OP评论的答案,ViewName
缺失是完全正常的,View
仍为空。 ViewName
仅在视图以名称返回的情况下才为空,如下所示:return View("Index");
。并且,ViewName
只是,而不是视图的整个路径。所以这不是解决方案。因此,要使此解决方案正常工作,您必须处理路由数据,控制器上下文等以查找视图。 (更多内容见下文。)
编辑:解决方案,注册自定义视图引擎
当MVC必须呈现视图时,它从路径数据,控制器上下文,视图名称(如上所述可以为空)获取信息,以及适用的约定。
特别是,在MVC中有一组注册视图引擎,需要查找调用FindView()
方法的视图。视图引擎将返回ViewEngineResult
,其中包含已找到的视图(如果找到),或者视图已被不成功地查找的路径列表。
因此,要修改模板路径,可以覆盖此功能:让原始类找到视图,如果找到,则修改路径。
要做秀,你需要采取以下步骤:
这是继承的视图引擎的代码:
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:在您阅读// Modify here !!
的地方,您可以修改result.View
的路径属性。将其投放到RazorView
:(result.View as RazorView).ViewPath
。由于ViewPath
setter受到保护,您需要使用Reflection进行设置:您可以使用SetViewPath
方法。
注意2:正如您所看到的,我不是继承RazorViewEngine
而是继承FixedRazorViewEngine
。如果您在MSDN中浏览此类,则不会得到结果,但如果查看已注册视图引擎列表的原始内容,您将找到此类。我认为这取决于项目中已安装的软件包,我认为它解决了MVC4中的一个错误。如果您未在Microsoft.Web.Mvc
命名空间中查找,请继承原始RazorViewEngined
注3:在找到视图后,视图引擎使用ViewEngineResult
执行它,因此,如果您更改它,它将使用新的视图路径 <执行/ p>
最后,您需要在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());
}
注意:如果你在ViewEngineConfig
文件夹中创建了一个App_Start
类,并且调用了这个类的静态方法,那就更干净了,就像在所有其他配置中一样。< / em>的
答案 1 :(得分:1)
答案是从here复制的。
如果您不介意将代码绑定到您正在使用的特定视图引擎,那么您可以查看ViewContext.View属性并将其强制转换为WebFormView
var viewPath = ((WebFormView)ViewContext.View).ViewPath;
我相信最终会得到你的观点名称。
编辑:Haacked绝对是现货;为了让事情变得更整洁,我把逻辑包装在一个像这样的扩展方法中: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"));
}
}
}
这似乎完全符合我的目的。
另一种解决方案here
((System.Web.Mvc.RazorView)htmlHelper.ViewContext.View).ViewPath
净-MVC
答案 2 :(得分:0)
我正在注册该地区,但是我不想要我的
url: "{area}/{controller}/{action}/{id}",
相反,我希望它像
url: "{controller}/{action}/{id}",
所以我已经注册了我的区域
context.MapRoute(
name: "AreaName_default",
url: "{controller}/{action}/{id}",
namespaces: new[] { "SolutionName.AreaName.Controllers" }
);
而且我不想在像
这样的每个操作方法中返回视图时添加硬代码字符串viewpathreturn View("~/Areas/AreaName/Views/ControllerName/ViewName.cshtml", model);
所以我创建了一个结果过滤器并覆盖了OnResultExecuting函数
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);
}
}
}