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等等。

你可能感兴趣的:(asp.net)