NVelocity for ASP.NET MVC

  在我的这篇博文中,有这么一段话:“我一直在想,有没有办法可以单独限制View中的代码的访问权限,类似于trust level,只是这个trust level是用来限制模板中的代码。”。有读者johngeng问,为什么要用trust level来锁住view,他不是很理解。我的本意是,希望在view中,开发人员只能写某一些特定功能的代码,调用某一些特定开放的API,对于大部分安全级比较高的代码,比如读写文件等API或类库,不允许在view当中使用。这对于我们将模板开放出来,在线提供给我们的用户去修改的需求下是非常重要的。而目前,不管WebForm还是Razor,都是非常自由的模板,在View能做的事情等同于Controller或其它地方所写的代码,这样View就不允许开放出来由用户在线修改。

  在相同的博文里面,还是那位读者johngeng提到它更喜欢$而不是@,由于我之前并不了解NVelocity,所以我误解为它是在说客户端开发包jquery。现在看来,他说的应该就是NVelocity,也许他觉得此人不可教,他并没有直接回复我的疑问,这也只能怪自己知识面太窄了。

  若不是最近在为项目添加多模板引擎的支持,或许我永远也无法得到以上两个问题的答案,而这两个答案都与NVelocity有关。虽然我平常肯定也见过NVelocity这个词,但到要选择除WebForm以外的模板引擎,我还是完完全全没有记起他,还是同事@浪子提醒我NVelocity这个模板引擎值得一试。看了官方的语法介绍后,我不得不说它是一种非常简洁且实用的模板,同时又不失它的灵活性和安全性。我所指的灵活性是它不像StringTemplate那样,限制的那么死,连个对象的函数都不允许调用。安全性方面又可以满足我希望模板上限制开发人员只能在模板上调用指定的API。到目前为止,NVelocity仍然让我非常满意。

  在ASP.NET MVC切换视图引擎非常简单,在ASP.NET MVC1.0出来以后,MvcContrib就曾经提供了多种视图引擎的切换选择,但是在最近的版本中,我却始终没有找到相关的代码,应该是这些代码已经被移出去了,但它的介绍文档中还没有删掉相关的主题。还好在@重典童鞋的博客上找到了他从MvcContrib中提取出来的实现。但是这个实现相对于MVC3来说,已经相对过时了,有些接口已经改变或被移除了,比如IViewLocator这个接口就已经不存在了。还有就是,它去掉了原先支持的调用HtmlHelper扩展方法的功能,而我最重要的就是要支持扩展函数,因为我自定义了一些必须的扩展方法。下面我们就来看看NVelocity for ASP.NET MVC几个类的详细情况:

  NVelocityViewEngine

  在之前的实现中,直接实现了IViewEngine这个接口,查找View的路径是通过实现IViewLocator来定位。在MVC2当中,修改了这部分的实现,MVC内部提供了VirtualPathProviderViewEngine这个模板方法类,在子类当中,我们中需要设置一下我们要查找的路径格式,其它的事件就可以交给模板方法类来完成,这样一方面可以简化我们的实现,另一方面还可以和默认的路径查找方式统一。

  同时,由于我使用Nvelocity内置的相对文件路径的方式来查找模板,而使用VirtualPath的风格,因此在找到VirtualPath后,我们需要转换成实际的物理路径,直接通过物理路径来加载模板内容,而内置的FileResourceLoader并不支持从物理路径加载模板,所以我们还要额外实现一下FileResourceLoader,让支持从物理路径的加载方法。这两个类的代码如下:

public class FileResourceLoaderEx : FileResourceLoader
{
public FileResourceLoaderEx() : base() { }
private Stream FindTemplate(string filePath)
{
try
{
FileInfo file
= new FileInfo(filePath);
return new BufferedStream(file.OpenRead());
}
catch (Exception exception)
{
base.runtimeServices.Debug(string.Format("FileResourceLoader : {0}", exception.Message));
return null;
}
}
public override long GetLastModified(global::NVelocity.Runtime.Resource.Resource resource)
{
if (File.Exists(resource.Name))
{
FileInfo file
= new FileInfo(resource.Name);
return file.LastWriteTime.Ticks;
}
return base.GetLastModified(resource);
}
public override Stream GetResourceStream(string templateName)
{
if (File.Exists(templateName))
{
return FindTemplate(templateName);
}
return base.GetResourceStream(templateName);
}
public override bool IsSourceModified(global::NVelocity.Runtime.Resource.Resource resource)
{
if (File.Exists(resource.Name))
{
FileInfo file
= new FileInfo(resource.Name);
return (!file.Exists || (file.LastWriteTime.Ticks != resource.LastModified));
}
return base.IsSourceModified(resource);
}
}
public class NVelocityViewEngine : VirtualPathProviderViewEngine, IViewEngine
{
public static NVelocityViewEngine Default = null;

private static readonly IDictionary DEFAULT_PROPERTIES = new Hashtable();
private readonly VelocityEngine _engine;

static NVelocityViewEngine()
{
string targetViewFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "views");
//DEFAULT_PROPERTIES.Add(RuntimeConstants.RESOURCE_LOADER, "file");
DEFAULT_PROPERTIES.Add(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, targetViewFolder);
DEFAULT_PROPERTIES.Add(
"file.resource.loader.class", "NVelocityEngine.FileResourceLoaderEx//,NVelocityEngine");
Default
= new NVelocityViewEngine();
}
public NVelocityViewEngine()
:
this(DEFAULT_PROPERTIES)
{
}
public NVelocityViewEngine(IDictionary properties)
{
base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.vm", "~/Views/Shared/{0}.vm" };
base.AreaMasterLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.vm", "~/Areas/{2}/Views/Shared/{0}.vm" };
base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.vm", "~/Views/Shared/{0}.vm" };
base.AreaViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.vm", "~/Areas/{2}/Views/Shared/{0}.vm" };
base.PartialViewLocationFormats = base.ViewLocationFormats;
base.AreaPartialViewLocationFormats = base.AreaViewLocationFormats;
base.FileExtensions = new string[] { "vm" };
if (properties == null) properties = DEFAULT_PROPERTIES;

ExtendedProperties props
= new ExtendedProperties();
foreach (string key in properties.Keys)
{
props.AddProperty(key, properties[key]);
}

_engine
= new VelocityEngine();
_engine.Init(props);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
Template viewTemplate
= GetTemplate(viewPath);
Template masterTemplate
= GetTemplate(masterPath);
NVelocityView view
= new NVelocityView(controllerContext, viewTemplate, masterTemplate);
return view;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
Template viewTemplate
= GetTemplate(partialPath);
NVelocityView view
= new NVelocityView(controllerContext, viewTemplate, null);
return view;
}
public Template GetTemplate(string viewPath)
{
if (string.IsNullOrEmpty(viewPath))
{
return null;
}
return _engine.GetTemplate(System.Web.Hosting.HostingEnvironment.MapPath(viewPath));
}
}

NET技术NVelocity for ASP.NET MVC,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。