DotLiquid模板引擎简介

DotLiquid是一个在.Net Framework上运行的模板引擎,采用Ruby的Liquid语法,这个语法广泛的用在Ruby on rails和Django等网页框架中。
DotLiquid相比于Mvc默认模板引擎Razor的好处有:

  • 因为不需要编译到程序集再载入

    • 首次渲染速度很快

    • 不会导致内存泄漏

  • 可以在任何地方使用

    • 不需要先准备WebViewPage,ViewContext等复杂的上下文对象

DotLiquid的官网是http://dotliquidmarkup.org/,开源协议是非常宽松的MS-PL。

示例代码

我创建一个使用了DotLiquid的示例Mvc项目,完整代码可以查看这里
以下的示例将以Mvc中的Action为单位,都存放在HomeController下。

最基础的使用

Template.Parse可以把字符串解析为模板对象,再使用Render把模板对象渲染为字符串。
打开页面可以看见Hello, World!

public ActionResult HelloWorld(){  

 var template = Template.Parse("Hello, {{ name }}!");  
  var result = template.Render(Hash.FromAnonymousObject(new { name = "World" }));  
  return Content(result); }

使用过滤器

|后面的就是过滤器,过滤器可以连锁起来使用。
escape过滤器用于做html编码,避免name中的"<"当成是html标签描画。
upcase过滤器把字符串中的字母全部转换为大写。
打开页面可以看见Hello, <WORLD>!

public ActionResult HelloFilter(){    var template = Template.Parse("Hello, {{ name | escape | upcase }}!");    var result = template.Render(Hash.FromAnonymousObject(new { name = "<World>" }));    return Content(result);
}

定义过滤器

DotLiquid支持自定义过滤器,首先需要一个过滤器类型,其中的函数名称就是过滤器名称。
过滤器支持多个参数和默认参数。

public class DotliquidCustomFilter{    public static string Substr(string value, int startIndex, int length = -1)    {        if (length >= 0)            return value.Substring(startIndex, length);        return value.Substring(startIndex);
    }
}

在网站启动的时候把这个过滤器注册到DotLiquid

public class MvcApplication : System.Web.HttpApplication{    protected void Application_Start()    {        // 在原有的代码下添加
        Template.RegisterFilter(typeof(DotliquidCustomFilter));
    }
}

这个例子会显示Hello, orl!

public ActionResult CustomFilter(){    var template = Template.Parse("Hello, {{ name | substr: 1, 3 }}!");    var result = template.Render(Hash.FromAnonymousObject(new { name = "World" }));    return Content(result);
}

使用标签

DotLiquid中有两种标签,一种是普通标签(Block),一种是自闭合标签(Tag)。
这里的assign是自闭合标签,if是普通标签,普通标签需要用end+标签名闭合。
显示内容是Hello, World!

public ActionResult HelloTag(){    var template = Template.Parse(@"        {% assign name = 'World' %}        {% if visible %}        Hello, {{ name }}!        {% endif %}    ");    var result = template.Render(Hash.FromAnonymousObject(new { visible = true }));    return Content(result);
}

自定义标签

这里我将定义一个自闭合标签conditional,这个标签有三个参数,如果第一个参数成立则描画第二个否则描画第三个参数。

public class ConditionalTag : Tag{   

 public string ConditionExpression { get; set; }    
 
 public string TrueExpression { get; set; }    public string FalseExpression { get; set; }  
 
 public override void Initialize(string tagName, string markup, List<string> tokens)    {      
  base.Initialize(tagName, markup, tokens);      
  var expressions = markup.Trim().Split(' ');        ConditionExpression = expressions[0];        TrueExpression = expressions[1];        FalseExpression = expressions.Length >= 3 ? expressions[2] : "";    }  
  
   public override void Render(Context context, TextWriter result)    {      
     var condition = context[ConditionExpression];      
      if (!(condition == null || condition.Equals(false) || condition.Equals("")))            result.Write(context[TrueExpression]);        else            result.Write(context[FalseExpression]);    } }

在网站启动时把这个标签注册到DotLiquid

public class MvcApplication : System.Web.HttpApplication{ 
   protected void Application_Start()    {        // 在原有的代码下添加        Template.RegisterTag<ConditionalTag>("conditional");    } }

这个例子会显示Bar

public ActionResult CustomTag(){ 
   var template = Template.Parse("{% conditional cond foo bar %}");
      var result = template.Render(Hash.FromAnonymousObject(new { cond = false, foo = "Foo", bar = "Bar" }));    return Content(result); }

模板文件

DotLiquid也支持从文件读取模板,需要先定义一个TemplateFileSystem

public class DotliquidTemplateFileSystem : IFileSystem{    public string ReadTemplateFile(Context context, string templateName)    {        var path = context[templateName] as string;        if (string.IsNullOrEmpty(path))            return path;        var fullPath = HttpContext.Current.Server.MapPath(path);        return File.ReadAllText(fullPath);
    }
}

设置DotLiquid使用自定义的文件系统

public class MvcApplication : System.Web.HttpApplication{    protected void Application_Start()    {        // 在原有的代码下添加
        Template.FileSystem = new DotliquidTemplateFileSystem();
    }
}

再定义一个控制器基类

public abstract class DotliquidController : Controller{   

 public ContentResult DotliquidView(string path = null, object parameters = null)  
 
{        // 路径为空时根据当前的Action决定        if (string.IsNullOrEmpty(path))        {            var controller = RouteData.Values["controller"];      
       var action = RouteData.Values["action"];            path = $"~/DotliquidViews/{controller}/{action}.html";        }        // 根据路径读取模板内容        var templateStr = Template.FileSystem.ReadTemplateFile(new Context(), "'" + path + "'");        // 解析模板,这里可以缓存Parse出来的对象,但是为了简单这里就略去了        var template = Template.Parse(templateStr);        // 描画模板        Hash templateParameters;    
       if (parameters is IDictionary<string, object>)            templateParameters = Hash.FromDictionary((IDictionary<string, object>)parameters);      
        else            templateParameters = Hash.FromAnonymousObject(parameters ?? new { });      
          var result = template.Render(templateParameters);        // 返回描画出来的内容        return Content(result, "text/html");    } }

现在可以在控制器中使用基于DotLiquid的模板了

public ActionResult HelloTemplateFile(){    return DotliquidView();
}

上面会返回文件~/DotliquidViews/Home/HelloTemplateFile.html的内容

Hello, Template!

嵌入子模板

为了实现代码的重用,DotLiquid的模板还可以嵌入其他子模板,嵌入需要使用include标签。
以下例子会显示Hello, Include!

public ActionResult HelloInclude(){    return DotliquidView();
}

文件~/DotliquidViews/Home/HelloInclude.html的内容

Hello, {% include "~/DotliquidViews/Home/HelloIncludeContents.html" %}!

文件~/DotliquidViews/Home/HelloIncludeContents.html的内容

Include

继承父模板

除了嵌入子模版,还能实现布局(Layout)方式的继承父模板,继承需要使用extends和block标签。
以下例子会返回Html<div class="layout"><h1>Here is title</h1><p>Here is body</p></div>

public ActionResult HelloExtends(){    return DotliquidView();
}

文件~/DotliquidViews/Home/HelloExtendsLayout.html的内容

<div class="layout">
    <h1>
        {% block title %}
        Default title
        {% endblock %}    </h1>
    <p>
        {% block body %}
        Default body
        {% endblock %}    </p></div>

文件~/DotliquidViews/Home/HelloExtends.html的内容

{% extends "~/DotliquidViews/Home/HelloExtendLayout.html" %}

{% block title %}
Here is title
{% endblock %}

{% block body %}
Here is body
{% endblock %}

描画自定义对象

请先看以下的例子

public class ExampleViewModel{   
 public string Name { get; set; }  
   public int Age { get; set; } }
public ActionResult CustomObject(){ 
   var template = Template.Parse("Name: {{ model.Name }}, Age: {{ model.Age }}");  
     var model = new ExampleViewModel() { Name = "john", Age = 35 };  
      var result = template.Render(Hash.FromAnonymousObject(new { model }));  
        return Content(result); }

你可能预料这个例子会显示Name: john, Age: 35,但实际运行时会给出以下错误

Name: Liquid syntax error: Object 'Dotliquid.Example.Dotliquid.ExampleViewModel' is invalid because it is neither a built-in type nor implements ILiquidizable, Age: Liquid syntax error: Object 'Dotliquid.Example.Dotliquid.ExampleViewModel' is invalid because it is neither a built-in type nor implements ILiquidizable

这是因为DotLiquid为了安全性,默认不允许描画未经注册的对象,这样即使模板由前端使用者提供也不会导致信息泄露。
为了解决上面的错误,需要把ExampleViewModel注册为可描画的对象。
除了使用RegisterSafeType注册,你也可以让ExampleViewModel继承ILiquidizable,在部分场景下会更适合。

public class MvcApplication : System.Web.HttpApplication{  
 protected void Application_Start()  
 
{        // 在原有的代码下添加        Template.RegisterSafeType(typeof(ExampleViewModel), Hash.FromAnonymousObject);    } }

写在最后

DotLiquid是一个灵活性很高并且依赖很少的模板引擎,虽然没有Razor流行,但大量的单元测试保证它可以经得起实际的使用。
目前使用了DotLiquid的项目有

目前DotLiquid准备升级2.0版本,作者正在召集PR,如果你有意向可以到DotLiquid的github看看。

原文地址:http://www.cnblogs.com/zkweb/p/5864794.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值