asp.net mvc(十一)自定义view engine

        当创建一个asp.net mvc 1.0的项目后,在web工程中都会出现Views文件夹,这里面就是我们存放View Page或者是partial view的地方。而且系统对于Controller的名称以及Views文件夹下面的子文件夹名称均有一定的约束。

        例如,有一个名称为GuestBookController的Controller,这时我们创建View Page时,就需要在Views下面创建一个GuestBook的子文件夹,不能随便命名。然后把View page或者是partial view放进来,或者把文件放在Shared文件夹中也行,如果把view page放在web工程根目录下面,系统会找不到对应的view。

        本文解决问题:针对上面的局限性,我们能不能打破呢?即可以实现如下功能:

        第一:view page可以放在views之外的文件夹中。

        第二:view page的名称和Controller的名称取消名称上的约束,例如:view page名称是ViewContentPage1.aspx,而对应的Controller名称是testController.cs。

        实现原理:asp.net mvc之所以能根据用户请求找到对应的view page,主要是路由。这其中有一个重要的类WebFormViewEngine,它负责发现我们创建的view page或者是partial view。为此我们可以重写一个新的ViewEngine来完成我的目的。

        1:先看下webformviewengine的构造函数:

代码
public WebFormViewEngine()
{
    
base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.master""~/Views/Shared/{0}.master" };
    
base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.aspx""~/Views/{1}/{0}.ascx""~/Views/Shared/{0}.aspx""~/Views/Shared/{0}.ascx" };
    
base.PartialViewLocationFormats = base.ViewLocationFormats;
}

        base.ViewLocationFormats 可以看出view page为什么只能写在views文件夹下的原因了。所以我们只需要在新的view engine的构造函数中修改下base.ViewLocationFormats 路径即可。这里我创建一个新的WebViewEngine,它需要继承WebFormViewEngine。可以这样改写:这样第一点就完成了。
   

代码
public WebViewEngine()
        {
            
base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.master""~/Views/Shared/{0}.master" };
            
base.ViewLocationFormats = new string[] { 
                 
"~/com/{0}.aspx",
                
"~/com/{0}.ascx",
                
"~/Views/{1}/{0}.aspx",
                
"~/Views/{1}/{0}.ascx"
                 
"~/Common/Views/{0}.aspx",
               
"~/Common/Views/{0}.ascx",
                
"~/Views/Shared/{0}.aspx",
                
"~/Views/Shared/{0}.ascx" };
            
base.PartialViewLocationFormats = base.ViewLocationFormats;
        }

         2:WebFormViewEngine有两个重要方法:FindPartialView以及FindView。它负责从用户请求以及路由配置中查找到具体的view page或者是partial view。第二点就可以在这下手。
         首先:我们需要修改下路由配置,增加一个参数viewpath,用来指定路由规则使用的view path路径。
      

代码
routes.MapRoute(
                
"Default2",
                
"ViewContentPage1.aspx",
                
new { id = "", viewpath = "~/com/ViewContentPage1.aspx", controller = "test", action = "test" }
            );

        其次:修改下WebFormViewEngine的源码:这里主要是修改GetPath方法,它最终会返回一个view page的路径。这样我们就可以这样访问了http://www.testmy.com/guestbook/ViewContentPage1.aspx
      

代码
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            
string[] strArray;
            
if (controllerContext == null)
            {
                
throw new ArgumentNullException("controllerContext");
            }
            
if (string.IsNullOrEmpty(partialViewName))
            {
                
throw new ArgumentException("MvcResources.Common_NullOrEmpty""partialViewName");
            }
            
string requiredString = controllerContext.RouteData.GetRequiredString("controller");
            
//string vp = this.GetParam(controllerContext, "viewpath");
            
//if (!string.IsNullOrEmpty(vp))
            
//{
            
//    requiredString = vp;
            
//}
            string str2 = this.GetPath(controllerContext, this.PartialViewLocationFormats, "PartialViewLocationFormats", partialViewName, requiredString, "Partial"

useCache,
false , out strArray);
            
if (string.IsNullOrEmpty(str2))
            {
                
return new ViewEngineResult(strArray);
            }
            
return new ViewEngineResult(this.CreatePartialView(controllerContext, str2), this);

        }
        
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            
string[] strArray;
            
string[] strArray2;
            
if (controllerContext == null)
            {
                
throw new ArgumentNullException("controllerContext");
            }
            
if (string.IsNullOrEmpty(viewName))
            {
                
throw new ArgumentException("ArgumentException""viewName");
            }
            
string requiredString = controllerContext.RouteData.GetRequiredString("controller");
            
//string vp=this.GetParam(controllerContext, "viewpath");
            
//if (!string.IsNullOrEmpty(vp))
            
//{
            
//    requiredString = vp;
            
//}
            string str2 = this.GetPath(controllerContext, this.ViewLocationFormats, "ViewLocationFormats", viewName, requiredString, "View", useCache,true , out strArray);
            
string str3 = this.GetPath(controllerContext, this.MasterLocationFormats, "MasterLocationFormats", masterName, requiredString, "Master", useCache,true , out 

strArray2);
            
if (string.IsNullOrEmpty(str2) || (string.IsNullOrEmpty(str3) && !string.IsNullOrEmpty(masterName)))
            {
                
return new ViewEngineResult(strArray.Union<string>(strArray2));
            }
            
return new ViewEngineResult(this.CreateView(controllerContext, str2, str3), this);

        }
        
private string GetParam(ControllerContext controllerContext, string key)
        {
            
return controllerContext.RouteData.Values[key] != null ? controllerContext.RouteData.Values[key].ToString() : string.Empty;
        }
        
private string GetPath(ControllerContext controllerContext, string[] locations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, 

bool useCache, bool isfindview, out string[] searchedLocations)
        {
            searchedLocations 
= _emptyLocations;
            
if (string.IsNullOrEmpty(name))
            {
                
return string.Empty;
            }
            
if ((locations == null|| (locations.Length == 0))
            {
                
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, "MvcResources.Common_PropertyCannotBeNullOrEmpty"new object[] { 

locationsPropertyName }));
            }
            
bool flag = IsSpecificPath(name);
            
string key = this.CreateCacheKey(cacheKeyPrefix, name, flag ? string.Empty : controllerName);
            
if (useCache)
            {
                
string viewLocation = this.ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);
                
if (viewLocation != null)
                {
                    
return viewLocation;
                }
            }
            
            
return (flag ? this.GetPathFromSpecificName(controllerContext, name, key,isfindview , ref searchedLocations) : this.GetPathFromGeneralName(controllerContext, 

locations, name, controllerName, key,isfindview , 
ref searchedLocations));
        }
        
private string CreateCacheKey(string prefix, string name, string controllerName)
        {
            
return string.Format(CultureInfo.InvariantCulture, ":ViewCacheEntry:{0}:{1}:{2}:{3}:"new object[] { base.GetType().AssemblyQualifiedName, prefix, name, 

controllerName });
        }
        
private static bool IsSpecificPath(string name)
        {
            
char ch = name[0];
            
return ((ch == '~'|| (ch == '/'));
        }
        
private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey,bool isfindview, ref string[] searchedLocations)
        {
            
string virtualPath = name;
            
if (!this.FileExists(controllerContext, name))
            {
                
if (isfindview)
                {
                    
string vp = this.GetParam(controllerContext, "viewpath");
                    
if (!string.IsNullOrEmpty(vp))
                    {
                        virtualPath 
= vp;
                    }
                    
if (string.IsNullOrEmpty(virtualPath))
                    {
                        virtualPath 
= string.Empty;
                        searchedLocations 
= new string[] { name };
                    }
                }
                
else
                {
                    virtualPath 
= string.Empty;
                    searchedLocations 
= new string[] { name };
 
                }
            }
            
this.ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
            
return virtualPath;
        }
        
private string GetPathFromGeneralName(ControllerContext controllerContext, string[] locations, string name, string controllerName, string cacheKey, bool isfindview, ref 

string[] searchedLocations)
        {
            
string virtualPath = string.Empty;
            searchedLocations 
= new string[locations.Length];
            
for (int i = 0; i < locations.Length; i++)
            {
                
string str2 = string.Format(CultureInfo.InvariantCulture, locations[i], new object[] { name, controllerName });
                
if (this.FileExists(controllerContext, str2))
                {
                    searchedLocations 
= _emptyLocations;
                    virtualPath 
= str2;
                    
this.ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
                    
return virtualPath;
                }
                
else
                {
                    
if (isfindview)
                    {
                        
string vp = this.GetParam(controllerContext, "viewpath");
                        
if (!string.IsNullOrEmpty(vp))
                        {
                            searchedLocations 
= _emptyLocations;
                            virtualPath 
= vp;
                            
return virtualPath;
                        }
                    }

                }
                searchedLocations[i] 
= str2;
            }
            
return virtualPath;
        }

           最后:在程序中注册新的view engine:        

            //注册viewEngine
            ViewEngines.Engines.Clear();
            ViewEngines.Engines.Add(
new
 WebViewEngine());
            RegisterRoutes(RouteTable.Routes);

 

           总结:很久没有更新mvc的文章了,不过这篇在实际项目中还是非常有用的,例如,我们可以把两个不同的view page指定同一个Controller等等。

posted on 2010-06-30 17:05  min.jiang  阅读(4617)  评论(6编辑  收藏  举报