博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ASP.NET MVC 4 视图页去哪里儿
阅读量:6829 次
发布时间:2019-06-26

本文共 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),之后再检查新地址下是否存在【视图页】。

注:默认情况下会先检查是否为手机端访问!

口说无凭,上源码吧:

 
VirtualPathProviderViewEngine
 
DisplayModeProvider
 
DefaultDisplayMode

由以上源码可知,默认情况下,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,如需转载请自行联系原作者

你可能感兴趣的文章
rpm方式安装zabbix2.4
查看>>
我的友情链接
查看>>
C# MVC中返回JSON 对象
查看>>
C#非托管内存的应用(一)——基本数据的拷贝
查看>>
中文分词原理
查看>>
详细探究Spark的shuffle实现
查看>>
我的友情链接
查看>>
提取android apk文件中的dat格式的图片资源
查看>>
windows 2003下 mysql安装时 MySQL Apply Security Settings安装不成功的解决方案
查看>>
【Oauth认证】使用scribe实现OAUTH
查看>>
ASP.NET Razor Pages API Quick Reference
查看>>
Chirp Chirp: Visual Studio Add-In for DotLess, Js, and Css Files
查看>>
安装MySQL5.6出现的问题
查看>>
我的友情链接
查看>>
cocos2dx程序启动过程,和跨平台实现
查看>>
75、分发系统|expect脚本远程登录和执行命令、传递参数
查看>>
系统集成资质培训 - 英语题目练习(0511)
查看>>
SSH连接速度慢
查看>>
Android ADB 常用命令
查看>>
Nagios/Cacti异常报警,设定总动清理内存
查看>>