zoukankan      html  css  js  c++  java
  • [ASPX] DotLiquid-Asp.net模板引擎

        以前用过一段时间的PHP,感觉非常不错,其中最让我难忘的就是Smarty模板引擎,当时就微微地想Asp.net里有没有像这样的模板引擎呢?不过由于之后的工作内容都用不到,或者说没有想到用模板,这想法也没导致我做一些事情,就不了了之了。

        现在也是工作需要,用模板是一个不错的选择。之前没用过这种东西,盲搜一片没找到自己想要的,于是自己就试着写写,大思路用的是Smarty的,用html页面做为模板,生成aspx页面,把数据放在HttpContext.Items里,代码如下:

    html模板:

    <div>{$title}</div>
    <select>
        {foreach $l in $list}
        <option value="{$l.Age}">{$l.Name}</option>
        {/foreach}
    </select>

        生成的aspx页面:

    复制代码
    <%@ Page Language="C#" %>
    <%
        if (HttpContext.Current.Items["SMARTY_TEMPLATE_DIR"]==null)
        {
            Response.Write("no direct access allowed");
            Response.End();
        }
    %>
    <div><%=DotSmarty.Smarty.GetTemplateArg("title"%></div>
    <% var list = DotSmarty.Smarty.GetTemplateArg("list"as System.Collections.Generic.IList<SmartyTest.User>%>
    <select>
        <% foreach (var l in list){ %>
        <option value="<%=l.Age %>"><%=l.Name %></option>
        <%}%>
    </select>
    复制代码

         调用如:

    复制代码
    Smarty smarty = new Smarty();
    List<User> list = new List<User>();
    list.Add(new User() {  Age=1, Name="name111"});
    list.Add(new User() { Age = 2, Name = "name222" });
    smarty.Assign("title""标题");
    smarty.Assign("list", list, TemplateArgType.List);
    smarty.Display("user/userInfo.htm");
    复制代码

         看起来很像Smarty,可越写难度越大!唉,能力有限,将来有能力再说吧,现在只能放弃。

         前几天幸运地听说了DotLiquid,网址是:http://dotliquidmarkup.org。上面曰:“DotLiquid is a templating system ported to the .net framework from Ruby’s Liquid Markup.It’s easy to learn, fast and safe"。我想我终于找到了asp.net中的smarty了,更有图说明:

    这里先介绍一下她的几个主要的概念:

     Filter:"Filters are simple methods(过滤器是一些简单的方法)"

         如标准Filter中的upcase,{{ "looGn" | upcase }}  值为"LOOGN"。

     Tag:"Tags are used for the logic in your template(标签用于实现模板中的逻辑)"。

         如标准Tag中的assign,{% assign freestyle = false %} 定义值为false的freestyle变量。

     Block:其实block也是tag,如if..else,for..in, 可以说Block是有endtag的Tag。

        如:{% for i in (1..5) %}

              {{ i }}

          {% endfor %}

        下面跟大家分享一下这几天对她的理解及代码实现:

        下载过发布的压缩包DotLiquid v1.5.zip里会有v3.5和v4.0两个版,引用DotLiquid.dll的对应版本到自己项目即可。其实她真的是easy to learn!看一个handler的代码:

    复制代码
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";
        Template template = Template.Parse("模板内容:{{hw}}");//用模板内容做为参数解析得到Template对象
        string result = template.Render(Hash.FromAnonymousObject(new { hw = "Hello World!" }));//用模板所需的元素做为参数呈现处理后的结果
        context.Response.Write(result);
    }
    复制代码

        一切的一切,其实就这两步!关键是!我们如何应用!"技术不是秘密,秘密是如何善用技术!" 不记得在哪看得一句话,不过无所谓啦!

        第一步: 从文件读入模板内容。模板大多数不是程序中的一个字符串变量,可能存在文件、数据库里等,这里说的是html文件模板。TemplateHelper如下

    复制代码
        public static class TemplateHelper
        {
            #region Template路径

            static TemplateHelper()
            {
                //可用文件配置,例子中用了字典
                _map = new Dictionary<stringstring>(50);
                _map.Add("master""~/template/master.htm");
                _map.Add("index_content_main""~/template/index_content_main.htm");
                _map.Add("list_content_main""~/template/list_content_main.htm");
                _map.Add("list_content_script""~/template/list_content_script.htm");
                _map.Add("detail""~/template/uc/detail.htm");
            }

            #endregion

            private static Dictionary<stringstring> _map;

            public static bool ContainsKey(string key)
            {
                return _map.ContainsKey(key);
            }
            public static string GetTemplateURL(string key)
            {
                try
                {
                    return _map[key];
                }
                catch (KeyNotFoundException e)
                {
                    KeyNotFoundException ne = new KeyNotFoundException(e.Message + "key:" + key);
                    throw ne;
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
            /*这个方法限制了文件模板路径必要配置,如果需要可以添加直接以文件路径为参数的方法,
             *不过感觉这里配置起来是个好的习惯 */
            public static Template GetFileTemplate(string templateKey, Encoding encoding)
            {
                Template template = HttpContext.Current.Cache[templateKey] as Template;
                if (template == null)
                {
                    string path = HttpContext.Current.Server.MapPath(GetTemplateURL(templateKey));
                    template = Template.Parse(File.ReadAllText(path, encoding));
                    CacheDependency dependency = new CacheDependency(path);
                    HttpContext.Current.Cache.Add(templateKey, template, dependency, Cache.NoAbsoluteExpiration,
                        Cache.NoSlidingExpiration, CacheItemPriority.Default, null);//把模板缓存起来
                }
                return template;
            }

            public static Template GetFileTemplate(string templateKey)
            {
                return GetFileTemplate(templateKey, Encoding.UTF8);
            }
        }
    复制代码

       

        第二步:include文件。DotLiquid的include是一个标准Tag,如{% include top %}。调用include需要给Template.FileSystem赋值,它是一个IFileSystem接口,在IFileSystem里只有一个方法:

        public interface IFileSystem
        {
            string ReadTemplateFile(Context context, string templateName);
        }

         很显然,解析到include top时会把top做为templateName调用Template.FileSystem.ReadTemplateFile方法,以此来实现include。所以实现IFileSystem的类要以templateName参数返回对应的内容:

    复制代码
        public class IncludeFileSystem : IFileSystem
        {
            private Encoding _encoding = Encoding.Default;

            public IncludeFileSystem(){}

            public IncludeFileSystem(Encoding encoding)
            {
                _encoding = encoding;
            }

            public string ReadTemplateFile(Context context, string templateName)
            {
                bool isOptional = false//是否可选
                string templateKey = templateName;
                if (templateName.EndsWith("_optional"))
                {
                    isOptional = true;
                    templateKey = templateKey.Replace("_optional""");
                }
                if (templateKey.StartsWith("content_"))
                {
                    object ns = context.Environments[0]["ns"];
                    if (ns == null)
                    {
                        ns = Path.GetFileNameWithoutExtension(HttpContext.Current.Request.RawUrl);
                    }
                    templateKey = ns + "_" + templateKey;
                }
                object result = HttpContext.Current.Cache[templateKey];
                if (result == null)
                {
                    if (isOptional && !TemplateHelper.ContainsKey(templateKey))
                    {
                        return string.Empty;
                    }
                    string path = HttpContext.Current.Server.MapPath(TemplateHelper.GetTemplateURL(templateKey));
                    result = File.ReadAllText(path, Encoding.UTF8);
                    CacheDependency dependency = new CacheDependency(path);
                    HttpContext.Current.Cache.Add(templateKey, result, dependency, Cache.NoAbsoluteExpiration, 
                        Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
                }
                return result.ToString();
            }
        }
    复制代码

        这里的ReadTemplateFile有 点复杂,其实就是根据templateName来读文件,缓存,至于开始的判断后面再说。在适当的地方设置一下Template.FileSystem 可。Template.FileSystem = new IncludeFileSystem();我写在了Application_Start事件处理程序里。

         第三步:提供测试数据。下面为了简单,没从数据库读取:

    复制代码
        public static class WebHelper
        {
            public static Family GetFamily()
            {
                Family family = new Family()
                {
                    Host = "孙悟空",
                    Address = "大佛山小石村",
                    Count = 4,
                    Desc = "快乐的一家人!"
                };
                return family;
            }

            public static List<Member> GetMembers()
            {
                List<Member> members = new List<Member>() { 
                    new Member(){ ID=1, Name="孙悟空", Age=42, Sex=true, Relation="本人", Desc="下等战士,重情重义、绝不欺骗朋友、喜欢帮助人,就算对着敌人也会帮助他。 多次救了地球和全人类。" },
                    new Member(){ ID=2, Name="牛琪琪", Age=40, Sex=false, Relation="妻子", Desc="孙悟空和琪琪结婚了,孙悟空没老可琪琪老了!"},
                    new Member(){ ID=3, Name="孙悟饭", Age=22, Sex=true , Relation="长子", Desc="拥有极高的潜力,每次遇到危险时都会发挥出强大力量来保护自己。"},
                    new Member(){ ID=4, Name="孙悟天", Age=15, Sex=true , Relation="次子", Desc="天赋极高,可老想着泡妞!"},
                };
                return members;
            }

            public static Member GetMemberInfo(int id)
            {
                return GetMembers().Single(m => m.ID == id);
            }
        }
        public class Family:Drop
        {
            public string Host { getset; }

            public string Address { getset; }

            public int Count { get; set; }        public string Desc { get; set; }    }    public class Member : Drop    {        public int ID { get; set; }        public string Name { get; set; }        public int Age { get; set; }        public bool Sex { get; set; }        public string Relation { get; set; }        public string Desc { get; set; }    }
    复制代码

        第四步:实现母版编程 。一直感觉Asp.net中母版很不错,Asp.net页面的对象模型来实际母版和用户控件也是顺理成章的事。要用DotLiquid的include标签来实现母版应该怎么做呢?这里说说我的解决方法。先看母版模板文件master.htm

    复制代码
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>{{title}}</title>
        <link href="/css/style.css" rel="stylesheet" type="text/css" />

        <script type="text/javascript" src="/js/jquery-1.6.1.min.js"></script>

        {% include content_script_optional %}
    </head>
    <body>
        <div id="root">
            <div class="top">
                <h2>
                    Top in master</h2>
            </div>
            {% include content_main %}
            <div class="bottom">
                <h2>
                    Bottom in master
                </h2>
            </div>
        </div>
    </body>
    </html>
    复制代码

       

        对,关键还是在templateName上,"content_main"和"content_script_optional" 中的"content"和"optional"是两个约定,还记得IncludeFileSystem.ReadTemplateFile方法吧,就是有点复杂的那个!content指明这里是包含"内容模板(名词来自内容窗体)",optional指明这个include可以没有,模板里到这就可以了,再看ReadTemplateFile方法有个object ns;默认是请求文件名,因为a页面可以用master.htm,b页面也可以用master.htm,ns默认就是"a"或"b",这样做只是为了找到a或b的模板。假如index_content_main.htm首页模板:

    复制代码
    <div class="center">
        <table class="tb" style="400px;">
            <caption>家庭:{{f.Host}}
            <href="/list.ashx">查看成员</a>
            </caption>
            <tr>
                <th>户主:</th><td>{{f.Host}}</td>
            </tr>
            <tr>
                <th>地址:</th><td>{{f.Address}}</td>
            </tr>
            <tr>
                <th>家庭成员数:</th><td>{{f.Count}}</td>
            </tr>
            <tr>
                <th>描述:</th><td>{{f.Desc}}</td>
            </tr>
        </table>
    </div>
    复制代码

        index.ashx为访问接口:

    复制代码
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            Template template = TemplateHelper.GetFileTemplate("master");
            string html = template.Render(Hash.FromAnonymousObject(new { title = "dotliquid demo index",f=WebHelper.GetFamily()}));
            context.Response.Write(html);
        }
    复制代码

        嘿嘿,这里有点怪吧,代码里看不出来和index页面有半点关系,template是根据master.htm得到的。这主要归功于那两个约定和一个ns默认值,如果把index.ashx改名为abc.ashx,上面呈现的代码就要这样写了:

    string html = template.Render(Hash.FromAnonymousObject(new {ns="index" , title = "dotliquid demo index",f=WebHelper.GetFamily()}));

         这几步到些完结!完整例子会附下载!

        

        扩展:还记得代码图右边的几个主要概念吧,下面是三个例子,我写的一个,他们的两个!

    复制代码
        public class TemplateFilters
        {
            public static string Text(bool? input, string trueText, string falseText, string nullText)
            {
                if (input == nullreturn nullText;
                return input == true ? trueText : falseText;
            }
            public static string Text(bool input, string trueText, string falseText)
            {
                return input ? trueText : falseText;
            }
        }
    复制代码

         这个是自己的Filters类,里面的每个方法都可以是一个Filter,{% true | text: "男" , "女" %} 此值为"男" ,调用第二个重载,text为方法名称(注意大小写),true是第一个参数input,"男"是第二个参数trueText,"女"是第三个参数 falseText。Filter要注册才可用,Template.RegisterFilter(typeof(TemplateFilters)); 这个我也写在Application_Start。

    复制代码
        public class Rd : Tag //随机数
        {
            int _max;
            public override void Initialize(string tagName, string markup, List<string> tokens)
            {
                base.Initialize(tagName, markup, tokens);
                _max = Convert.ToInt32(markup);
            }
            public override void Render(Context context, StreamWriter result)
            {
                result.Write(new Random().Next(_max).ToString());
            }
        }

        public class Scale : Block//概率出现,用的单词应该不当!~
        {
            int _max;
            public override void Initialize(string tagName, string markup, List<string> tokens)
            {
                base.Initialize(tagName, markup, tokens);
                _max = Convert.ToInt32(markup);
            }
            public override void Render(Context context, StreamWriter result)
            {
                if (new Random().Next(_max) == 0)
                    base.Render(context, result);
            }
        }
    复制代码

        还是要先注册,Template.RegisterTag<Rd>("rd");Template.RegisterTag<Scale>("scale");

        用法分别是  {% rd 10 %} 值为0到10任意一个数字, {% scale 10 %}这里有10%的概率出现{% endscale %}。

        下载

         其实MVC也不错哦

    --------------------------------------

    欢迎您,进入 我系程序猿 的cnBlog博客。

    你不能改变你的过去,但你可以让你的未来变得更美好。一旦时间浪费了,生命就浪费了。

    You cannot improve your past, but you can improve your future. Once time is wasted, life is wasted.

    --------------------------------------

    分享到QQ空间  

  • 相关阅读:
    luogu P1162 填涂颜色 x
    【説明する】素数
    codevs 4064 组合 x
    codevs 2039 骑马修栅栏 USACO x
    codevs 2038 香甜的黄油x+luogu P1828 x
    [HIHO1079]离散化(线段树、染色)
    [HIHO1196]高斯消元·二(高斯消元、枚举自由变元)
    hihoCoder太阁最新面经算法竞赛17
    [POJ1753]Flip Game(异或方程组,高斯消元,枚举自由变量)
    [POJ1681]Painter's Problem(高斯消元,异或方程组,状压枚举)
  • 原文地址:https://www.cnblogs.com/jqmtony/p/4150866.html
Copyright © 2011-2022 走看看