美文网首页扩展技术程序员Java学习笔记
SpringBoot+Thymeleaf实现html文件引入(类

SpringBoot+Thymeleaf实现html文件引入(类

作者: 朱端的一坨 | 来源:发表于2016-06-15 00:12 被阅读39497次

    由于对高大上的前端处理不太熟悉,想直接通过MVC的方式进行内容传递,因此选用了Thymeleaf模版处理向前端传值的问题。
    但是觉得很多PHP框架所实现的include模版的方式很不错,能够很好的实现头文件、导航、页尾等和主要内容的解耦,因此想通过使用Thymeleaf模版的同时,也能实现类似于include的功能。


    思路和背景

    觉得如果有精力,最好还是RESTFul的结构,然后前端采用angularjs之类的方式来处理可能更合理,由于不太懂前端,所以只是自己yy一下

    言归正传,环境主要是采用了spring boot框架,外加Thymeleaf进行构建的。主要的思路就是通过使用Thymeleaf的"th:utext()"方式,在后端处理中,将预留的头文件、导航等通过ModelMap映射到实际的网页中。


    实际操作

    1. 背景

    首先配置一个可以运行的Spring boot+Thymeleaf项目,具体教程很多,就不啰嗦了。

    然后采用了默认的目录配置,大致如下:
    resource
      static ——(默认的静态文件目录)
      templates ——(默认的Thymeleaf模版目录)
      application.properties

    2. 基本处理

    之后的在模版文件中需要include到其它内容的部分采用th:utext()函数把相关部分标注出来,并且在controller中把相关内容加载到ModelMap中,大概是这个样子的
    模版文件部分:

    <header class="am-topbar admin-header" th:utext="${headerContent}">
      <div class="am-topbar-brand">
        <strong>创澳商务</strong> <small>交易者系统</small>
      </div>  
    </header>
    

    后台处理部分:

    @RequestMapping("/background")
        private String backgroud(ModelMap map){
            map.addAttribute("headerContent", "hello word!");
            return "background";
        }
    

    3. 所见即所得的方式

    因为经常可能需要修改模版文件(html)使得达到预期的效果,而Thymeleaf等框架最大的一个特典就是前端的设计和后端的实现可以得以分离 (因为直接使用的是html的文件,即使不需要服务器也能通过浏览器直接浏览设计好的模版文件),所以我们就希望能够引入的也是html文件,这也符合include功能的基本构思。所以在项目中引入了Jsoup包来解决遍历dom文件的问题。当然这个完全过程完全可以略过,直接用个txt或者json啥的

    当然也可以选择其它广泛使用的dom遍历工具,只是在这里想安利一下这个jsoup这个包。jsoup的选择器语法和JQuery等前端工具的语法基本一致,对于习惯了JQuery的人真是一个福音……
    具体配置如下:
    加入maven依赖

          <dependency>
              <groupId>org.jsoup</groupId>
              <artifactId>jsoup</artifactId>
              <version>1.9.2</version>
          </dependency>
    

    采用selector获取element,得到内容

    map.addAttribute("headerContent", doc.select("header").toString());
    map.addAttribute("sideBarContent", doc.select("#admin-offcanvas").first().toString());
    

    通过这么操作之后,就实现了可以通过html直接设计页面,之后通过后台程序include到前端页面了。

    4. include头部引用

    对于一般的导航部分(如div)等,只要进行进行了以上处理就ok了,但是既然是想实现include类似的功能,分离head中的各种引用就自然是必须的了。不然每次如果改了需要引用的css或者js都要重新每个文件都要修改,想想都头疼。

    但是问题在于,在通过使用ModelMap引用头部link的时候,下面这种处理是得不到解析的。那么我们的css和js的引用自然就乱套了。

    th:href="@{/css/main.css}"
    

    解决的方案应该很多,我在处理的时候,主要是通过利用框架自身的特点,将所有的css文件都放在了static文件夹里面(spring boot默认存放的静态资源的地方),然后直接通过写死href值的方式来解决这一问题的(href="css/main.css")。

    当然我觉得应该可能还有更好的方案,也请大家指教一下~

    5. static下面css文件可能不一致的问题

    虽然我们解决了link和script等引入文件路径的问题,但是将href写死又引入了新一个问题,我们没有办法直接使用static下面的css文件,因为html文件都处于templates下面,而且如果我们想在templates下面新建跟多文件夹,虽然通过服务器能够正常引入到正确的css文件,但是不能直接通过浏览器进行页面的设计和编辑,也就是违反了我们之前提到的前端的设计和后端的实现分离的特点。

    为了进一步解决这个问题,虽然可以简单的就在templates文件夹下面拷贝一份css文件(如果想新建更多子文件夹的,那每个文件夹下面都需要拷贝一份css文件),但是显然这个方案一点都不够优雅,而且可能会由于忘记同步css文件造成服务器显示和设计显示不一致的问题。

    那么为了要优雅,不要污,我采用了一种新的方案,就是html文件中直接写入到static文件夹下css的href,而再服务器引入的时候,通过将static等前缀进行删除,来得到新的href的方式,保证了使用同一份css的同时,也可以分离前端的设计和后端的实现。而且由于路径中都含有static这个特征,直接使用substring功能真是爽翻了。具体实现如下:

       public void getNavigation(Integer naviType,ModelMap map){               
           String fileUrl = templateUrl;
           switch(naviType){
               case 1:
                   //admin navigatioin
                   fileUrl = fileUrl + "header_admin.html";
                   break;
               case 2:
                   //user navigation
                   fileUrl = fileUrl + "header_trader.html";
                   break;
               default:
                   fileUrl = fileUrl + "header_trader.html";
                   break;
           }       
           try {
               InputStream input = this.getClass().getResourceAsStream(fileUrl);
               Document doc = Jsoup.parse(input,"UTF-8","http://www.mychuangao.com/");
               map.addAttribute("headerContent", doc.select("header").toString());
               map.addAttribute("sideBarContent", doc.select("#admin-offcanvas").first().toString());
               map.addAttribute("footerContent", changeAttrAddress(doc.select("footer"),"script","src"));
               map.addAttribute("headContent", changeAttrAddress(doc.select("head"),"link","href"));
           } catch (IOException e) {
               logger.error("error when using jsoup");
               logger.error(e.getMessage());
               map.addAttribute("headContent", "");
               map.addAttribute("headerContent", "");
               map.addAttribute("sideBarContent", "");
               map.addAttribute("footerContent", "");
           }
       }
       
       private String changeAttrAddress(Elements parentNode,String dealingNodeName,String attrName){
           Elements elements = parentNode.select(dealingNodeName);
           for(Element elment : elements){
               String orignalAddress = elment.attr(attrName);
               if(orignalAddress.isEmpty()){
                   continue;
               }
               orignalAddress = delInnerPath(orignalAddress);
               elment.attr(attrName,orignalAddress);
           }
           return parentNode.toString();
       }
       
       private String delInnerPath(String address){
           String keyWord = "static";
           if(address.contains(keyWord)==false){
               return address;
           }
           int position = address.indexOf(keyWord) + keyWord.length() + 1;
           return address.substring(position);
       }
    

    注意点

    1. 注意要使用th:utext()函数,而非th:text()函数,原因你懂的~
    2. 处理路径时,如果使用了获取绝对路径的方式,可能会造成访问权限问题,在web项目中要慎重
    3. 大家可以根据自己的需求更改spring boot和Thymeleaf的文件目录,具体可以参考修改spring boot默认目录

    相关文章

      网友评论

      • Carvendy许:可以导入外部的html页面吗?
      • chenssy:兄弟,这种实现你确定你不会累死?而且要后台干涉前台:smiley: :smiley:
      • c4914b0006dd:hi,楼主.请问下这种情况怎么处理.我的公用菜单是动态从后端获取的,然后怎么在模版中引入公用菜单的这个页面?
        朱端的一坨:粗略想了下,我觉得你就直接将内容放入到map里面,然后模版里面解析这个值就可以了吧,应该和正常的传值没啥区别吧。
      • 见云开:这样写,你就不觉得丑陋?
        朱端的一坨:@陌羲 嗯嗯,对thymleaf这套不是很熟悉,确实通过官方的这种引用写起来更简洁,感谢分享~
        面皮大师:兄弟,看看这篇文章吧,你这么写有点丑陋了
        http://www.jianshu.com/p/ed9d47f92e37
        朱端的一坨:嗯嗯,有啥更好的写法请指教~ 多向大神学习~
      • 锁_0405:页面引入css,js,正确写法,href="../static/css/test.css" th:href="@{/css/test.css}"
        朱端的一坨:是想说用后面的 th:href="@{/css/test.css}" 代替前面的 href="../static/css/test.css" 吗?
        时间太久了,我其实记的不是很清楚了,不过记的应该是由于spring默认的路径处理和直接的嵌套路径会导致@{/css/test.css}这种写法需要冗余一份static文件,所以才通过程序里面又处理了一道的。
      • VilenEera:我想这篇文章也能解决需要 include 的问题.
        http://zslin.com/web/article/detail/32
      • VilenEera:"注意要使用th:untext()函数,而非th:text()函数"
        这里的 untext 应该改成 utext
        朱端的一坨:@VilenEera 恩恩,是的,笔误,感谢指正
      • 庸人视角:请问一下thymeleaf可以引入远程跨域的html吗?
        朱端的一坨:@永远在晴天 我理解跨域和模版这些应该没啥关系吧,按照一般的跨域配置就ok了吧
      • ace14f655ef2:`th:fragment`,` th:include`你用过没。。。
        朱端的一坨:@诗书不负人 这个没试过呢
      • ec9d51f4b1c9:你好具体的相关代码怎么实现的能给一个实例吗
        朱端的一坨:@勿念勿思 您是想要一个demo么,我之前的不记得弄哪去了,别抱太大希望的找找看哈

      本文标题:SpringBoot+Thymeleaf实现html文件引入(类

      本文链接:https://www.haomeiwen.com/subject/vnbudttx.html