ASP.NET MVC Preview 2 - NVelocityViewEngine
在前面做 MVC 流程分析的时候,我曾提到过,我更喜欢用 Velocity 来代替 WebForm。这倒不是说 WebForm 本身有什么不好,更不是某些人所说的 "性能" 原因。在实际开发中,我们所面对的页面往往很复杂,有诸多看上去很头疼的 div、style、span 什么的,我想没有几个 "程序员" 愿意去调整某些个边框布局或者颜色设置。WebForm 基于设计和封装的原因,会使用大量的 "非标准 HTML 代码",这就造成了一个尴尬的局面 —— 美工和页面程序员的矛盾。
美 工: "你把我设计的页面全搞乱了,你应该用这个样式……"
程序员: "废话,早跟你说过了。你必须按这样的格式来写……"
美 工: "我修改了页面,已经发给你了……"
程序员: "没空,你自己不会改啊?"
Velocity 是什么,无需我多言。使用一种非常简单的标记或逻辑控制就可以将我们的数据 "嵌入" 到美工设计的页面中。我们可以让美工事先将需要输出数据的位置用 "<!-- 数据1 -->" 之类的东东标注出来,我们也只需关心数据是否正确输出,至于页面的调整和我们基本没有关系。当然,对于有一定兴趣的美工或者组里面的新人,完全可以在一天甚至半天内掌握 Velocity 的使用方法,何乐而不为?要知道,多数网站会频繁调整页面显示,不像我们通常用来演示的那几个 Example 那么简单。
要使用自定义视图引擎,我们实现需要找到 "注入" 位置。我们在前面的流程分析时,就已经锁定目标 —— ControllerBuilder。
public class GlobalApplication : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
ControllerBuilder.Current.SetControllerFactory(new NVelocityContrillerFactory());
}
}
NVelocityContrillerFactory 可以直接继承自 DefaultControllerFactory,我们 override 它的方法,将生成的 Controller.ViewEngine 赋值为我们自定义的视图引擎即可。
public class NVelocityContrillerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
var controller = base.GetControllerInstance(controllerType);
(controller as Controller).ViewEngine = new NVelocityViewEngine();
return controller;
}
}
接下来,我们开始写我们自己的视图引擎。
public class NVelocityViewEngine : IViewEngine
{
static NVelocityViewEngine()
{
Velocity.SetProperty("resource.loader", "file");
Velocity.SetProperty("file.resource.loader.path", HttpContext.Current.Server.MapPath("~"));
Velocity.SetProperty("file.resource.loader.cache", true);
//Velocity.SetProperty("file.resource.loader.modificationCheckInterval", 10L); //seconds
Velocity.SetProperty("input.encoding", "utf-8");
Velocity.Init();
}
public void RenderView(ViewContext viewContext)
{
var locator = new NVelocityViewLocator() as IViewLocator;
var templatePath = locator.GetViewLocation(viewContext, viewContext.ViewName);
var template = Velocity.GetTemplate(templatePath);
var context = new VelocityContext();
if (viewContext.ViewData != null)
{
var properties = viewContext.ViewData.GetProperties(); // 扩展方法,获取全部属性。
foreach (var item in properties.Keys)
{
context.Put(item, properties[item]);
}
}
template.Merge(context, viewContext.HttpContext.Response.Output);
}
}
也很简单,除了在静态构造里面初始化 NVelocity 以外,我们还对 ViewData 做了些处理。当然,我们最好再来一个 NVelocityViewLocator,以便支持从多个目录中找寻模板文件。
public class NVelocityViewLocator : ViewLocator
{
public NVelocityViewLocator()
{
this.ViewLocationFormats = new[]
{
"/Views/{1}/{0}.vm",
"/Views/{1}/{0}.htm",
"/Views/{1}/{0}.html",
"/Views/Shared/{0}.vm",
"/Views/Shared/{0}.htm",
"/Views/Shared/{0}.html",
};
this.MasterLocationFormats = new string[0];
}
}
我们可以支持更多的扩展名,呵呵~~~~~
好了,测试一下。
public class HomeController : Controller
{
public void Index()
{
this.RenderView("index", new { Title = "TemplateEngine Test", Name = "Tomcat" });
}
}
~/View/Home/index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/....dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>$Title</title>
</head>
<body>
Hello, $Name!
</body>
</html>
输出结果
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/....dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>TemplateEngine Test</title>
</head>
<body>
Hello, Tomcat!
</body>
</html>
忘了一件重要的事情,NVelocity 可以在 Castle MonoRail 的包里找到。