本文共 6227 字,大约阅读时间需要 20 分钟。
这里特别感谢 提到了Displaymodeprovider,所以才有了本篇博客,也使我对【View的呈现】中寻找视图页的过程有了清晰的认识!
在MVC中,执行完Action之后,会返回一个ActionResult对象,之后再执行该对象的ExecuteResult方法,这也就是【View的呈现】的入口!
【View的呈现】包括了:根据模版去寻找请求的视图页、编译视图页、再执行视图页的内容。本篇就来介绍寻找视图页的详细过程,其中涉及到了MVC 4的一个新特性--“手机视图页”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | public abstract class ViewResultBase : ActionResult { public override void ExecuteResult(ControllerContext context) { if (context == null ) { throw new ArgumentNullException( "context" ); } if (String.IsNullOrEmpty(ViewName)) { ViewName = context.RouteData.GetRequiredString( "action" ); } ViewEngineResult result = null ; if (View == null ) { //通过视图引擎去创建视图对象,并将视图对象和该视图相关的信息封装在ViewEngineResult对象中。 result = FindView(context); View = result.View; } TextWriter writer = context.HttpContext.Response.Output; ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer); View.Render(viewContext, writer); if (result != null ) { result.ViewEngine.ReleaseView(context, View); } } } public class ViewResult : ViewResultBase { protected override ViewEngineResult FindView(ControllerContext context) { //寻找当前请求的视图页,如果能找到则创建视图对象。 //遍历每个视图引擎(默认有两个WebFormEngine、RazorViewEngine),并执行每一个视图引擎的FindView方法。 ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName); //如果创建了视图对象,即:指定路径中存在相匹配的视图页(.cshtml文件)。 if (result.View != null ) { return result; } //没有创建视图对象,即:指定路径中不存在相匹配的视图页(.cshtml文件)。 StringBuilder locationsText = new StringBuilder(); foreach ( string location in result.SearchedLocations) { locationsText.AppendLine(); locationsText.Append(location); } throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.Common_ViewNotFound, ViewName, locationsText)); } } |
ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);则是遍历每个视图引擎(默认有两个WebFormEngine、RazorViewEngine),并执行每一个视图引擎的FindView方法。
注:在执行视图引擎的FindView方法时,先按照从缓存表中找是否存在请求的视图页,如果没有的话,再进行一次寻找!下面以RazorViewEngine为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public abstract class VirtualPathProviderViewEngine : IViewEngine { //useCache先是true,该方法返回的是null。则让useCache=false,再执行一遍。即:先通过缓存去找,如果没有找到的话,就正经的去找。 public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) { if (controllerContext == null ) { throw new ArgumentNullException( "controllerContext" ); } if (String.IsNullOrEmpty(viewName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName" ); } string [] viewLocationsSearched; string [] masterLocationsSearched; string controllerName = controllerContext.RouteData.GetRequiredString( "controller" ); //获取视图的路径,这里就是咱们本篇博文的内容的入口点!!!!!!!!!!!!!!!!!!! //ViewLocationFormats、AreaViewLocationFormats定义在派生类RazorViewEngine类中。 //ViewLocationFormats:"~/Views/{1}/{0}.cshtml","~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml","~/Views/Shared/{0}.vbhtml" //AreaViewLocationFormats:"~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml","~/Areas/{2}/Views/Shared/{0}.vbhtml" string viewPath = GetPath(controllerContext, ViewLocationFormats, AreaViewLocationFormats, "ViewLocationFormats" , viewName, controllerName, CacheKeyPrefixView, useCache, out viewLocationsSearched); string masterPath = GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats, "MasterLocationFormats" , masterName, controllerName, CacheKeyPrefixMaster, useCache, out masterLocationsSearched); if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName))) { return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched)); } return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this ); } } |
GetPath方法在寻找【视图页】时,首先将当前请求的Controller和Action的名称添加到地址格式化器中,这样就有了要寻找的地址(们),之后就来检查格式化后的地址是否真的存在指定的【视图页】。如果是通过手机端来请求,则会对格式化之后的地址进行再进行处理(如:Index.cshtml修改为Index.Mobile.cshtml),之后再检查新地址下是否存在【视图页】。
注:默认情况下会先检查是否为手机端访问!
口说无凭,上源码吧:
由以上源码可知,默认情况下,ASP.NET MVC4在DisplayModeProvider中定义了一个含有两个DefaultDisplayMode对象(用于对地址再处理)的集合:
1 2 3 4 5 6 7 8 9 | public static readonly string MobileDisplayModeId = "Mobile" ; private readonly List<IDisplayMode> _displayModes = new List<IDisplayMode> { new DefaultDisplayMode(MobileDisplayModeId) { ContextCondition = context => context.GetOverriddenBrowser().IsMobileDevice }, new DefaultDisplayMode() }; |
由于处理时,是按照遍历执行_displayModes集合中DefaultDisplayMode对象的GetDisplayInfo方法(索引值从0开始),所以无论是 PC 还是 Phone发送的请求,都会先执集合中的第一个DefaultDisplayMode对象(判断是否为手机的请求)。如果Phone端发送请求,会去寻找xxx.Mobile.cshtml,如果没有的话,就继续执行第二个DefaultDisplayMode对象,去寻找xxx.cshtml。如果是PC端发送请求,也是首先执行第一个DefaultDisplayMode对象,但是由于不满足 context => context.GetOverriddenBrowser().IsMobileDevice 条件,所以还是需要去执行第二个DefaultDisplayMode对象,去寻找xxx.cshtml。
扩展:
1、指定DisplayMode模拟需求:对Phone端用户的某个Action请求,返回电脑版网页。
1 2 3 4 5 6 7 | public ActionResult Index() { //一些判断条件 this .ControllerContext.DisplayMode = DisplayModeProvider.Instance.Modes[1]; DisplayModeProvider.Instance.RequireConsistentDisplayMode = true ; return View(); } |
根据上述设置,即使是Phone端的请求并且还存在Index.Mobile.cshtml文件,也会去执行Index.cshtml,即:实现Phone用户访问电脑版网页。
2、自定义DisplayMode
模拟需求:为Android 2.3用户设置特定的页面
先创建一个类似于Index.Android23.cshtml 的页面,然后在Global.asax中做如下设置即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); AuthConfig.RegisterAuth(); DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode( "Android23" ) { ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf ( "Android 2.3" , StringComparison.OrdinalIgnoreCase) >= 0) }); } } |
以上就是所有内容,如有不适之处,请指正!!!
本文转自武沛齐博客园博客,原文链接:http://www.cnblogs.com/wupeiqi/p/3597845.html,如需转载请自行联系原作者