zoukankan      html  css  js  c++  java
  • 关于xml作为模板的配置服务系统开发

      最近在做一个后台配置系统,其实之前也接触过,所谓的配置系统就是指,将你的网站布局抽象成一个xml模板,里面包括你自定义的节点,然后将变化的部分作为配置项,通过服务将配置选项与模板组装成一个js(这个服务你可以分离出去,也就是分布式部署,这样的话系统需要引用其客户端进行访问,也可以耦合在一起,这个自己看情况选择),用户只需引用js(当然需要在页面上加上事前约定好的dom节点),然后显示。

      这种需求一般是一些网站前端经常变化,然后需要每次开发人员改动html后都需要发布站点,比较繁琐。

      今天主要介绍一下核心的拼接服务,xml模板是存放到数据库中的,因为每次都去读数据库中的xml很繁琐,我们是在保存xml模板的时候形成了cache缓存,每当模板更新保存的时候触发更新缓存(当然也设置了缓存过期时间)。

    组装模板如下:

    //load xml
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(template);
    //childItemTemplate
    XmlNode childTpl = doc.SelectSingleNode("leftMenu").SelectSingleNode("XXName");
    Razor.Compile(childTpl.InnerText, typeof(IDictionary<string, string>), string.Format("XXName{0}", OO));
    //container
    XmlNode containerTpl = doc.SelectSingleNode("leftMenu").SelectSingleNode("container");
    Razor.Compile(containerTpl.InnerText, typeof(IDictionary<string, string>), string.Format("CCC_{0}", CCC));

    //levelOneMenuTemplate
    var levelOneChilds = doc.SelectSingleNode("leftMenu").SelectSingleNode("levelOneMenuTemplate").ChildNodes;
    Parallel.For(0, levelOneChilds.Count, i =>
    {
    XmlElement xe;
    var node = levelOneChilds[i];
    if (node is XmlElement)
    {
    xe = (XmlElement)node;
    Razor.Compile(xe.InnerText, typeof(IDictionary<string, string>), string.Format("levelOneMenuTemplate_{0}_{1}", xe.GetAttribute("index"), templateId));
    }
    });

    //SetCache
    Nav.Common.CacheHelper.SetCache(string.Format("IsWriteComplied_{0}", templateId), true, Cache.NoAbsoluteExpiration, TimeSpan.FromDays(30));

    其中,Razor.Compile()是生成Razor缓存的机制,有很多重载函数,但是本次用到的是

     public static void Compile(string razorTemplate, Type modelType, string cacheName);
    

    最终生成的代码如下:思路也是比较简单,首先循环遍历一级菜单,在判断该菜单有无子节点,如果有就再循环遍历其菜单子节点,将子节点append在一级菜单下面,当然本系统有个最大显示数,就是不是所有的

    节点都要显示的,只有需要显示的时候才显示(这个用js控制),所有在组装的时候,多加了一个标签是sbHiddenChild,这个就是隐藏的子节点string

     private string GenerateFinalMenu(MenuEntity entity)
            {
                StringBuilder sbDisplayChild = new StringBuilder();
                StringBuilder sbHiddenChild = new StringBuilder();
                StringBuilder sbLevelOne = new StringBuilder();
                string rst = string.Empty;
                //load xml 
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(entity.HtmlTemplate);
                XmlNode root = doc.SelectSingleNode("leftMenu");
                var levelOneMenuTemplate = root.SelectSingleNode("levelOneMenuTemplate");
                var childItemTemplate = root.SelectSingleNode("childItemTemplate").InnerText;
                Common.Helper.TemplateCompiler.CompileWriteTemplate(entity.HtmlTemplate, entity.HtmlTemplateId, false);
                //组装子节点
                foreach (var item in levelOneMenuTemplate.ChildNodes)
                {
                    sbDisplayChild.Clear();
                    sbHiddenChild.Clear();
                    XmlElement xe;
                    if (item is XmlElement)
                    {
                        xe = (XmlElement)item;
                        var menuIndex = int.Parse(xe.GetAttribute("index"));
                        //可显示最大子节点
                        int maxChild = xe.HasAttribute("maxChild") ? int.Parse(xe.GetAttribute("maxChild")) : -1;
                        if (xe.GetAttribute("hasChild") == "true")
                        {
                            var subMenuItems = entity.MenuItems[menuIndex].SubMenuItems;
                            for (int i = 0; i < subMenuItems.Count; i++)
                            {
                                //child to display
                                if ((maxChild > 0 && maxChild > i) || maxChild == -1)
                                {
                                    sbDisplayChild.Append(Razor.Run<IDictionary<string, string>>(string.Format("childItemTemplate_{0}", entity.HtmlTemplateId),
                                                            new Dictionary<string, string> {{ "Href", subMenuItems[i].ItemHref },
                                                                                        { "Id", subMenuItems[i].ItemValue },
                                                                                        { "Class", subMenuItems[i].ItemStyle },
                                                                                        { "Name", subMenuItems[i].ItemName }}));
                                }
                                else//child to hide
                                {
                                    sbHiddenChild.Append(Razor.Run<IDictionary<string, string>>(string.Format("childItemTemplate_{0}", entity.HtmlTemplateId),
                                                            new Dictionary<string, string> {{ "Href", subMenuItems[i].ItemHref },
                                                                                        { "Id", subMenuItems[i].ItemValue },
                                                                                        { "Class", subMenuItems[i].ItemStyle },
                                                                                        { "Name", subMenuItems[i].ItemName }}));
                                }
                            }
    
                            sbLevelOne.Append(Razor.Run<IDictionary<string, string>>(string.Format("levelOneMenuTemplate_{0}_{1}", menuIndex.ToString(), entity.HtmlTemplateId),
                                                            new Dictionary<string, string> {{ "DisplayItems", sbDisplayChild.ToString()},
                                                                                        { "HiddenItems", sbHiddenChild.ToString()}}));
                        }
                        else
                        {
                            sbLevelOne.Append(xe.InnerText);
                        }
                    }
    
    
    
                };
                rst = Razor.Run<IDictionary<string, string>>(string.Format("container_{0}", entity.HtmlTemplateId), new Dictionary<string, string> { { "MenuItems", sbLevelOne.ToString() } });
                return rst;
            }

    我们在使用的时候,通过要压缩一下 

    dbEnity.MenuContent = Nav.Common.WebHelper.Compress(this.GenerateFinalMenu(entity));
    public static string Compress(string strHTML)
            {
                Regex reg = new Regex(@"(?<=^|>)[^<>]+(?=<|$)");
                Regex regSpa = new Regex(@"^s+|s+$");
                return reg.Replace(strHTML, delegate(Match m) { return regSpa.Replace(m.Value, ""); });
            }

    压缩完成后,保存在数据库中。至此,配置服务可以说完成了一半了,接下来,就是生成一个js,并把js发布掉。

    生成js其实很简单的,第一步:我们用的是web api 返回一个HttpResponseMessage类的对象,但是他的主要数据是result啊,那这个result从哪来的呢?

    [HttpGet]
            public HttpResponseMessage GetContent(string e = "")
            {
                INavMenuVersionService svc = IoC.Resolve<INavMenuVersionService>();
                string result = svc.GetOnlineJsContent();
                var ecode = System.Text.Encoding.UTF8;
                if(!string.IsNullOrEmpty(e))
                {
                    ecode = System.Text.Encoding.GetEncoding("gb2312");
                }
                var resp = new HttpResponseMessage(HttpStatusCode.OK);
                resp.Content = new StringContent(result, ecode, "text/plain");
                return resp;
            }

    看看result是从哪来的吧,对的,就是上面我们出来拼接后的数据,我们存放到MenuContent中的

      public string GetOnlineJsContent()
            {
                try
                {
                    string rst = string.Empty;
                    Dictionary<string, string> contents = new Dictionary<string, string>();
                    SearchParams sp = new SearchParams { States = (int)VersionState.Online };
                    //get all version list
                    var verList = this.GetNavMenuVersionList(sp);
    
                    if (verList != null && verList.Data != null)
                    {
                        foreach (var v in verList.Data)
                        {
                            contents.Add(v.MenuType.ToString(), HttpUtility.HtmlEncode(Nav.Common.WebHelper.Compress(v.MenuContent).Trim()));
                        }
    
                        //complie read template
                        //load template xml
                        rst = BuildTemplateAndMenuContent(verList.Data[0], contents);
                    }
                    return rst + C_END_TAG;
                }
                catch
                {
                    return string.Empty;
                }
            }

    看上面的代码,我们发现其实处理的不是一个菜单啊,貌似VersionState.Online的都处理了啊,这个。。这个是我们故意的,拿到MenuContent之后并不是直接用来生成js,因为MenuContent只是一个html

    格式啊,并不是一个js所以在上面中使用BuildTemplateAndMenuContent()方法进行Build一下,该方法主要是下面这一句:

    string rst = Razor.Run<IDictionary<string, string>>("jsTemplate_" + versionData.MenuType,
                                                            new Dictionary<string, string> { { "MenuContent", Newtonsoft.Json.JsonConvert.SerializeObject(contents) } });
    private string BuildTemplateAndMenuContent(INavMenuVersionExtEntity versionData,Dictionary<string, string> contents)
            {
           //创建cache
    var menuEntity = Newtonsoft.Json.JsonConvert.DeserializeObject<MenuEntity>(versionData.MenuSchema); XmlDocument doc = new XmlDocument(); doc.LoadXml(menuEntity.HtmlTemplate); Common.Helper.TemplateCompiler.CompileReadTemplate(doc, menuEntity.MenuType);
           //build,主要是加上菜单的类型
    string rst = Razor.Run<IDictionary<string, string>>("jsTemplate_" + versionData.MenuType, new Dictionary<string, string> { { "MenuContent", Newtonsoft.Json.JsonConvert.SerializeObject(contents) } }); return rst; }
    上面的方法中其他的代码其实都是为了创建缓存用的(在此不做介绍),其中,menuEntity是我们的配置保存的菜单所有项(包含了一级导航,二级子节点和模板内容),之所以加上菜单类型
    主要是因为我们生成的js是所用Online菜单数据,这样方便用户多选择使用,只需要传入一个MenuType就能获取自己想要的数据(因为我们就只有一个js,不然一种菜单一种js也是可以实现的)
    最后发布即可。。。。
  • 相关阅读:
    java_类承继其他类的内部类例子
    java_接口和抽象类的区别
    java_数组作缓存池的不可变类实例
    C++_归并排序(纯C版)
    C++_归并排序
    C++_快速排序(纯C版本)
    C++_快速排序
    C++_直接插入排序(纯C版)
    C++_直接插入排序
    自定义比较器的用法
  • 原文地址:https://www.cnblogs.com/walt/p/5080494.html
Copyright © 2011-2022 走看看