Beetl学习总结(4)——Web集成

4.1. Web提供的全局变量

Web集成模块向模板提供web标准的变量,做如下说明

  • request 中的所有attribute.在模板中可以直接通过attribute name 来引用,如在controller层 request.setAttribute("user",user),则在模板中可以直接用${user.name} .

  • session 提供了session会话,模板通过session["name"],或者session.name 引用session里的变量

  • request 标准的HTTPSerlvetRequest,可以在模板里引用request属性(getter),如${request.requestURL}。

  • parameter 用户读取用户提交的参数。如${parameter.userId} (仅仅2.2.7以上版本支持)

  • ctxPath Web应用ContextPath

  • servlet 是WebVariable的实例,包含了HTTPSession,HTTPSerlvetRequest,HTTPSerlvetResponse.三个属性,模板中可以通过request.response,session 来引用,如 ${serlvet.request.requestURL};

  • 所有的GroupTemplate的共享变量

  • pageCtx是一个内置方法 ,仅仅在web开发中,用于设置一个变量,然后可以在页面渲染过程中,调用此api获取,如pageCtx("title","用户添加页面"),在其后任何地方,可以pageCtx("title") 获取该变量。(仅仅2.2.7以上版本支持)

你可以在模板任何地方访问这些变量

如果你需要扩展更多属性,你也可以配置beetl.properties配置文件的WEBAPP_EXT属性,实现WebRenderExt接口,在渲染模板之前增加自己的扩展,如:

1
2
RESOURCE.root=/WEB-INF/views
WEBAPP_EXT = com.park.oss.util.GlobalExt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class GlobalExt implements WebRenderExt{

        static long version = System.currentTimeMillis();
        @Override
        public void modify(Template template, GroupTemplate arg1, HttpServletRequest arg2, HttpServletResponse arg3) {
                //js,css 的版本编号
                template.binding("sysVersion",version);
        }

}

这样,每次在模板里都可以访问变量sysVersion了,不需要再controller里设置,或者通过servlet filter来设置

4.2. 集成技术开发指南

Beetl默认提供了WebRender用于帮助web集成开发,所有内置的集成均基于此方法。如果你认为Beetl内置的各个web框架集成功能不够,你可以继承此类,或者参考此类源码重新写,其代码如下

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package org.beetl.ext.web;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.exception.BeetlException;

/**
 *  通常web渲染的类,将request变量赋值给模板,同时赋值的还有session,request,ctxPath
 *  其他框架可以继承此类做更多的定制
 * @author joelli
 *
 */
public class WebRender
{
        GroupTemplate gt = null;

        public WebRender(GroupTemplate gt)
        {
                this.gt = gt;
        }

        /**
         * @param key 模板资源id
         * @param request
         * @param response
         * @param args 其他参数,将会传给modifyTemplate方法
         */
        public void render(String key, HttpServletRequest request, HttpServletResponse response, Object... args)
        {
                Writer writer = null;
                OutputStream os = null;
                try

                {
                        //                      response.setContentType(contentType);
                        Template template = gt.getTemplate(key);
                        Enumeration<String> attrs = request.getAttributeNames();

                        while (attrs.hasMoreElements())
                        {
                                String attrName = attrs.nextElement();
                                template.binding(attrName, request.getAttribute(attrName));

                        }
                        WebVariable webVariable = new WebVariable();
                        webVariable.setRequest(request);
                        webVariable.setResponse(response);
                        webVariable.setSession(request.getSession());

                        template.binding("session", new SessionWrapper(webVariable.getSession()));

                        template.binding("servlet", webVariable);
                        template.binding("request", request);
                        template.binding("ctxPath", request.getContextPath());

                        modifyTemplate(template, key, request, response, args);

                        String strWebAppExt = gt.getConf().getWebAppExt();
                        if(strWebAppExt!=null){
                                WebRenderExt renderExt = this.getWebRenderExt(strWebAppExt);
                                renderExt.modify(template, gt, request, response);
                        }
                        if (gt.getConf().isDirectByteOutput())
                        {
                                os = response.getOutputStream();
                                template.renderTo(os);
                        }
                        else
                        {
                                writer = response.getWriter();
                                template.renderTo(writer);
                        }

                }
                catch (IOException e)
                {
                        handleClientError(e);
                }
                catch (BeetlException e)
                {
                        handleBeetlException(e);
                }

                finally
                {
                        try
                        {
                                if (writer != null)
                                        writer.flush();
                                if (os != null)
                                {
                                        os.flush();
                                }
                        }
                        catch (IOException e)
                        {
                                handleClientError(e);
                        }
                }
        }

        /**
         * 可以添加更多的绑定
         * @param template 模板
         * @param key 模板的资源id
         * @param request
         * @param response
         * @param args  调用render的时候传的参数
         */
        protected void modifyTemplate(Template template, String key, HttpServletRequest request,
                        HttpServletResponse response, Object... args)
        {

        }

        /**处理客户端抛出的IO异常
         * @param ex
         */
        protected void handleClientError(IOException ex)
        {
                //do nothing
        }

        /**处理客户端抛出的IO异常
         * @param ex
         */
        protected void handleBeetlException(BeetlException ex)
        {
                throw ex;
        }
}

4.3. Serlvet集成

只需要在Servlet代码里引用ServletGroupTemplate就能集成Beetl,他提供了一个render(String child, HttpServletRequest request, HttpServletResponse response)方法。例子如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
protected void doGet(HttpServletRequest request,
                        HttpServletResponse response) throws ServletException, IOException {

                response.setContentType("text/html;charset=UTF-8");
                //模板直接访问users
                request.setAttribute("users",service.getUsers());
                ServletGroupTemplate.instance().render("/index.html", request, response);


        }

ServletGroupTemplate同其他web集成一样,将读取配置文件来配置,如果需要通过代码配置,可以在Serlvet listener里 ServletGroupTemplate.instance().getGroupTemplate()方法获取GroupTemplate

4.4. SpringMVC集成

需要做如下配置即可

1
2
3
4
5
6
<bean id="beetlConfig" class="org.beetl.ext.spring.BeetlGroupUtilConfiguration" init-method="init"/>


<bean id="viewResolver" class="org.beetl.ext.spring.BeetlSpringViewResolver">
        <property name="contentType" value="text/html;charset=UTF-8"/>
</bean>

同其他集成方式一样,模板的配置将放在beetl.properties中。

如果想获取GroupTemplate,可以调用如下代码

1
2
3
BeetlGroupUtilConfiguration config = (BeetlGroupUtilConfiguration) this.getApplicationContext().getBean(
                                "beetlConfig");
GroupTemplate group = config.getGroupTemplate();

Controller代码如下:

1
2
3
4
5
6
7
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(HttpServletRequest req) {
        ModelAndView view = new ModelAndView("/index");
        //total 是模板的全局变量,可以直接访问
        view.addObject("total",service.getCount());
  return view;
}

4.5. SpringMVC集成高级

spring集成还允许注册被spring容器管理的Function,Tag等,也还允许配置多个视图解析器等功能

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<bean name="beetlConfig" class="org.beetl.ext.spring.BeetlGroupUtilConfiguration" init-method="init">
        <property name="configFileResource" value="/WEB-INF/beetl.properties"/>
        <property name="functions">
             <map>
               <entry key="testFunction" value-ref="testFunction"/>
            </map>
        </property>

        <property name="functionPackages">
             <map>
               <entry key="fp" value-ref="testFunctionPackage"/>
            </map>
        </property>

        <property name="tagFactorys">
             <map>
               <entry key="html.output" value-ref="testTagFactory"/>
               <entry key="html.output2" value-ref="testTagFactory2"/>
            </map>
        </property>

</bean>

<bean name="testTagFactory" class="org.beetl.ext.spring.SpringBeanTagFactory">
     <property name="name" value="testTag"/>
</bean>
<bean name="testTagFactory2" class="org.beetl.ext.spring.SpringBeanTagFactory">
     <property name="name" value="testTag2"/>
</bean>


<bean name="beetlViewResolver" class="org.beetl.ext.spring.BeetlSpringViewResolver">
        <property name="config" ref="beetlConfig"/>
        <property name="contentType" value="text/html;charset=UTF-8"/>
</bean>

如上图所示,BeetlGroupUtilConfiguration有很多属性,列举如下

  • configFileResource 属性指定了配置文件所在路径,如果不指定,则默认在classpath下

  • functions 指定了被spring容器管理的function,key为注册的方法名,value-ref 指定的bean的名称

  • functionPackages,指定了被spring容器管理的functionPackage,key为注册的方法包名,value-ref 指定的bean的名称

  • tagFactorys ,注册tag类,key是tag类的名称,value-ref指向一个org.beetl.ext.spring.SpringBeanTagFactory实例,该子类是一个Spring管理的Bean。属性name对应的bean就是tag类。需要注意,由于Tag是有状态的,因此,必须申明Scope为 "prototype"。如代码:

1
2
3
4
@Service
@Scope("prototype")
public class TestTag extends Tag {
}
  • typeFormats: 同functions,参数是 Map<Class<?>, Format>,其中key为类型Class

  • formats:同functions,参数是 Map<String, Format>,其中key为格式化函数名

  • virtualClassAttributes 同functions,参数Map<Class<?>, VirtualClassAttribute>,其中key为类型Class

  • virtualAttributeEvals ,类型为List<VirtualAttributeEval>

  • resourceLoader,资源加载器 ,值是 实现ResourceLoader的一个Bean

  • errorHandler ,错误处理,值是实现ErrorHandler的一个Bean

  • sharedVars,同functions,类型是Map<String, Object>,可以在此设置共享变量

  • configProperties,类型是Properties,可以覆盖配置文件的某些属性

如下配置,指定了三个视图解析器,一个用于beetl页面渲染,一个用于cms,采用了beetl技术,另外一个一些遗留的页面采用jsp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<bean name="beetlConfig" class="org.beetl.ext.spring.BeetlGroupUtilConfiguration" init-method="init">
        <property name="configFileResource" value="/WEB-INF/beetl.properties"/>
</bean>


<bean name="cmsbeetlConfig" class="org.beetl.ext.spring.BeetlGroupUtilConfiguration" init-method="init">
        <property name="configFileResource" value="/WEB-INF/cms-beetl.properties"/>
</bean>


<!-- Beetl视图解析器1 -->
<bean name="beetlViewResolver" class="org.beetl.ext.spring.BeetlSpringViewResolver">
        <!-- 多视图解析器,需要设置viewNames和order -->
        <property name="viewNames">
                <list>
                        <value>/template/**</value>
                </list>
        </property>
        <property name="suffix" value=".btl"/>
        <property name="contentType" value="text/html;charset=UTF-8"/>
        <property name="order" value="0"/>
        <!-- 多GroupTemplate,需要指定使用的bean -->
        <property name="config" ref="beetlConfig"/>

</bean>

<!-- Beetl视图解析器2 -->
<bean name="cmsBeetlViewResolver" class="org.beetl.ext.spring.BeetlSpringViewResolver">
        <!-- 多视图解析器,需要设置viewNames和order -->
        <property name="viewNames">
                <list>
                        <value>/cmstemplate/**</value>
                </list>
        </property>
        <property name="contentType" value="text/html;charset=UTF-8"/>
        <property name="order" value="1"/>
        <!-- 多GroupTemplate,需要指定使用的bean -->
        <property name="config" ref="cmsbeetlConfig"/>

</bean>

<!-- JSP视图解析器 -->
<bean name="JSPViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 注意JSP的这个视图解析器order必须在最后 -->
        <property name="order" value="256"/>
        <!-- beetl配置不支持前缀,这不同于jsp 和 freemaker -->
        <property name="prefix" value="/WEB-INF/"/>
        <property name="suffix" value=".jsp"/>
        <property name="contentType" value="text/html;charset=UTF-8"/>
</bean>

Beetl视图解析器属性同spring自带的视图解析器一样,支持contentType,order,prefix,suffix等属性。

注意视图解析器里属性viewNames,这个用于判断controller返回的path到底应该交给哪个视图解析器来做。

  • 以/template开头的是beetlViewResolver来渲染。

  • 以/cmstemplate是交给cmsBeetlViewResolver渲染。

  • 如果都没有匹配上,则是jsp渲染

如果你想更改此规则,你只能增加canHandle方法指定你的逻辑了。详情参考org.springframework.web.servlet.view.UrlBasedViewResolver.canHandle

对于仅仅需要redirect和forward的那些请求,需要加上相应的前缀

  • 以"redirect:"为前缀时:表示重定向,不产生BeetlView渲染模版,而直接通过Servlet的机制返回重定向响应.redirect:前缀后面的内容为重定向地址,可以采用相对地址(相对当前url),绝对地址(完整的url),如果采用/开头的地址,会自动的在前面接上当前Web应用的contextPath,即contextPath为test的Web应用中使用redirect:/admin/login.html 实际重定向地址为 /test/admin/login.html

  • 以"forward:"为前缀时:表示转发,不产生BeetlView渲染模版。而是直接通过Servlet的机制转发请求(关于转发和重定向的区别,请自行查看Servlet API) forward:前缀后面的内容为转发地址,一般都是以/开头相对于当前Web应用的根目录

其他集成需要注意的事项:

  • spring集成,请不要使用spring的 前缀配置,改用beetl的RESOURCE.ROOT 配置,否则include,layout会找不到模板

4.6. Spring Boot集成

Spring Boot 通过java config来配置 beetl需要的BeetlGroupUtilConfiguration,和 BeetlSpringViewResolver,参考代码如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Configuration
public class BeetlConf {


        @Value("${beetl.templatesPath}") String templatesPath;//模板跟目录

        @Bean(initMethod = "init", name = "beetlConfig")
    public BeetlGroupUtilConfiguration getBeetlGroupUtilConfiguration() {
        BeetlGroupUtilConfiguration beetlGroupUtilConfiguration = new BeetlGroupUtilConfiguration();
        try {
            ClasspathResourceLoader cploder = new ClasspathResourceLoader(BeetlConf.class.getClassLoader(),templatesPath);
            beetlGroupUtilConfiguration.setResourceLoader(cploder);
            return beetlGroupUtilConfiguration;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    @Bean(name = "beetlViewResolver")
    public BeetlSpringViewResolver getBeetlSpringViewResolver(@Qualifier("beetlConfig") BeetlGroupUtilConfiguration beetlGroupUtilConfiguration) {
        BeetlSpringViewResolver beetlSpringViewResolver = new BeetlSpringViewResolver();
        beetlSpringViewResolver.setContentType("text/html;charset=UTF-8");
        beetlSpringViewResolver.setOrder(0);
        beetlSpringViewResolver.setConfig(beetlGroupUtilConfiguration);
        return beetlSpringViewResolver;
    }

 }

spring boot集成需要注意的是要添加spring-devtools.properties文件,并配置如下选项

1
2
restart.include.beetl=/beetl-xxx.jar
restart.include.beetlsql=/beetlsql-xxx..jar

spring-devtools.properties 为spring boot的配置文件,位于META-INF目录下

4.7. Jodd集成

需要配置web.xml,将所有请求交给jodd处理,参考:http://jodd.org/doc/madvoc/setup.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<filter>
        <filter-name>madvoc</filter-name>
        <filter-class>jodd.madvoc.MadvocServletFilter</filter-class>
        <init-param>
                        <param-name>madvoc.webapp</param-name>
                        <param-value>test.MyWebApplication</param-value>
          </init-param>
    <init-param>
                <param-name>madvoc.configurator</param-name>
                <param-value>test.MyAutomagicMadvocConfigurator</param-value>
        </init-param>

</filter>
<filter-mapping>
        <filter-name>madvoc</filter-name>
        <url-pattern>/*</url-pattern>
</filter-mapping>

MyWebApplication 和 MyAutomagicMadvocConfigurator 需要自己参照如下例子写一个,前者用来设置beetl作为视图渲染,后者配置Jodd不要扫描beetl struts集成里引用的struts类

1
2
3
4
5
6
7
8
9
public class MyAutomagicMadvocConfigurator extends AutomagicMadvocConfigurator {
        public MyAutomagicMadvocConfigurator(){
                super();
                //不扫描beetl 里jar文件里的action和result,否则,会扫描StrutsResultSupport不相干的class
                this.rulesJars.exclude("**/*beetl*.jar");


        }
}
1
2
3
4
5
6
7
8
public class MyWebApplication  extends WebApplication{
     @Override
     protected void init(MadvocConfig madvocConfig, ServletContext servletContext) {
                 //设置默认
         madvocConfig.setDefaultActionResult(BeetlActionResult.class);

     }
 }

最后,可以写Action了,浏览器输入/index.html,jodd将执行world方法,并渲染ok.html模板。如果你想配置GroupTemplate,正如其他集成框架一样,只需要写一个beetl.properties 即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@MadvocAction
public class IndexAction {

     @Out
     String value;
     @Action("/index.html")
     public String world() {

         value = "Hello World!";
         return "/ok.html";
     }
}

4.8. JFinal集成

Beetl提供 JFinal 集成,使用BeetlRenderFactory ,通过如下注册即可使用beetl模板引擎

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import org.beetl.ext.jfinal.BeetlRenderFactory
public class DemoConfig extends JFinalConfig
{
        public void configConstant(Constants me)
        {

                me.setMainRenderFactory(new BeetlRenderFactory());
                // 获取GroupTemplate ,可以设置共享变量等操作
                GroupTemplate groupTemplate = BeetlRenderFactory.groupTemplate ;

}

业务逻辑代码:

1
2
3
4
5
6
7
8
9
        public void modify(){
                int artId = getParaToInt(0, -1);
                setAttr("title", "修改文章");
                List<Cate> cateLists = Cate.getAllCate();
                //模板里访问cateLists,atr,
                setAttr("cateLists", cateLists);
                setAttr("art", Article.dao.findById(artId));
                render("/modify.html");
        }

BeetlRenderFactory 默认使用FileResourceLoader ,其根目录位于WebRoot目录下,如果你需要修改到别的目录,可以设置配置文件,如

1
RESOURCE.root= /WEB-INF/template/

https://git.oschina.net/xiandafu/beetl-jfinal-sample 有完整例子,采用jfinal+beetl写的一个博客系统

4.9. Nutz集成

Nutz集成提供了 BeetlViewMaker ,实现了 ViewMaker方法,如下代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
        @At("/ctx")
        @Ok("beetl:ctx.btl")
        public Context withContext() {
                Context ctx = Lang.context();
                Pager pager = dao.createPager(1, 20);
                pager.setRecordCount(dao.count(UserProfile.class));
                List<UserProfile> list = dao.query(UserProfile.class, null, pager);
                ctx.set("pager", pager);
                ctx.set("list", list);
                return ctx;
        }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<html>
<head>
<title>Beetl&Nutz</title>
</head>
<body>
<p>总共 ${list.~size}<p/>
<%
for(user in list){
%>
<p>hello,${user.nickname};<p/>
<%}%>

<p>当前页${pager.pageNumber},总共${pager.pageCount}页<p/>
</body>
</html>

需要注意的是,如果使用了nutz的obj(http://www.nutzam.com/core/mvc/view.html),则需要在模板顶部申明obj是动态对象,如

1
2
3
4
5
6
<%
directive dynamic obj
%>

${obj.user.title}
${obj.user.name}

4.10. Struts2集成

需要在struts2配置文件里添加result-types做如下配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<package name="default" namespace="/" extends="struts-default">
.......
<result-types>
        <result-type name="beetl"
                                class="org.beetl.ext.struts2.Struts2BeetlActionResult" default="true" >
                                <param name="contentType">text/html; charset=UTF-8</param>
        </result-type>
</result-types>

<action name="HelloWorld" class="com.beetl.struts.HelloWorld">
    <result>/hello.html</result>
</action>
<action name="Ajax" class="com.beetl.struts.AjaxHtml">
        <result>/table.html#table</result>
</action>
........
</package>

该类会根据struts配置文件获取模板,如上例的hello.html,并将formbean的属性,以及request属性作为全局变量传递给模板

郑重申明

鉴于struts2有安全漏洞,而官方补丁打法很消极,所以请谨慎使用Struts2,Beetl的安全性已经通过在线体验和多个使用Beetl的网站得以体现 一旦你的struts2网站被攻破,请先确定是否是struts2 的问题

4.11. 直接Web中运行Beetl模板

对于web应用来说,必须通过controller才能渲染模板,beetl也可以写完模板后,在未完成controller情况下,直接渲染模板 此方法既可以作为通常的全栈式开发人员使用,也可以用于前端人员单独开发模板用。

步骤如下:

  • 配置监听器,监听器指定对*.btl的请求进行监听(假定模板名字都是以btl.结尾)。

  • 实现监听器,该监听器继承父类 org.beetl.ext.web.SimpleCrossFilter,实现protected abstract GroupTemplate getGroupTemplate()方法。依据不同的集成方式,比如你的环境是Servlet,则只需要调用ServletGroupTemplate.instance().getGroupTemplate(),如果是Jfinal,需要调用BeetlRenderFactory.groupTemplate等

  • SimpleCrossFilter 提供一些有用的方法,可以帮助你定制一些特性,可以参考源码了解

  • 置完成后,对于要测试的模板,可以新建一个对应的伪模型文件,比如要测试模板WebRoot/user/userList.html,可以新建立WebRoot/values/user/userList.html.var 。 values是监听器默认的伪模型的根目录

  • 编辑伪模型文件,对应于userList.html需要的全局变量,userList.html.var可以申明这些些变量

1
2
3
var proudct = {id:1,name:'测试产品',pic:'xxxx.jpg'};
var userList = [{id:2,name:'用户一'}];
var session= {admin:{id:1,name:'admin'}};
  • 通过浏览器直接访问http://ip:port/user/userList.html,监听器会预先执行userList.html.var,并将返回值作为模板的全局变量,传给userList.html

  • 可以将一些公共的变量放到WebRoot/values/common.var里(比如上面代码的session). 监听器会先执行common.var,然后再执行userList.html.var

直接访问模板前提是使用了伪模型,这与实际的项目采用的模型并不一致,因此当模板采用伪模型验证后,需要重启web应用,才能使用真正的模型去测试,否则,模板引擎会报错,这是因为beetl默认的FastRuntimeEngine会根据模型优化模板,对同一个模板不同的模型会报错,除非采用DefaultTemplateEngine 或者页面申明类型变量是动态的。

4.12. 整合ajax的局部渲染技术

越来越多web网站依赖于ajax,如table的翻页,流行方式是浏览器发出ajax请求,后台处理后返回一个json,浏览器端将json数据拆开,拼成一条一条的行数据,然后生成dom节点,追加到表格里。 作为另外一种可选技术,beetl支持局部渲染技术,允许后台处理返回的是一个完成的html片段,这样,前端浏览器可以直接将这个html片段追加到表格里。在我做的性能测试里,俩种方式性能差别不大(http://beetlajax.oschina.mopaas.com/)

比如模板index.html有很多动态内容,有动态生成的菜单,有右侧的top10,也有核心区域的表格,大概内容如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<#menu/>
<#top10> ....</#top10>
<div id="table-container" >
<%
//ajax片段开始
#ajax userTable: {
%>

<table>
        <tr><td width=100>id</td><td width=100>姓名</td></tr>
        <%for(user in users){%>
        <tr><td>${user.id}</td><td>${user.name}</td></tr>
        <%}%>
</table>

当前页面<span id="current">${page!1}</span><span style="20px"></span>
<a href="#"><span  class="page">next</span></a> <a href="#" ><span  class="page">pre</span></a>
<%
//ajax片段结尾
}
%>

#ajax 用于告诉告诉模板引擎,此处是个局部渲染标记,标记为"userTable",对于正常渲染视图"index.html"页面,#ajax标记没什么用处,table仍能得到正常渲染。如果渲染的视图是index.html#userTable,则模板只会渲染#ajax标记得模板片段,其他部分将忽略。关于完整例子,可以参考http://beetlajax.oschina.mopaas.com/

后台代码如下:

1
render("/index.html#userTable");

ajax 片段渲染也支持默认情况下不渲染,仅仅做为一个片段使用,如一个页面有许多后台交互操作,并返回相应的html片段,可以将这些html片段也放到同一个模板里,使用ajax norender,表示渲染整个模板的时候默认并不需要渲染此ajax片段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<%
<html>

</html>
#ajax norender success: {
%>
<div id="success"> 操作成功
</div>

<%
}
%>

#ajax norender failure: {
%>
<div id="failure"> 操作失败
</div>

<%
}
%>

这样,此页面默认情况下并没有输出success,和 failure片段

注意,Ajax片段本质上是从模版的ajax标记处开始渲染,因此,ajax需要的变量在模版里也必须是全局变量,如果你只是个局部变量,beetl会报出找不到变量,即使你binding了这个变量,beetl也认为这个是局部变量,如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
" >
<%
var tableData = paras.table;
#ajax userTable: {
for(user in tableData);
%>

<%
//ajax片段结尾
}
%>

变量tableData是从paras里获取的,是个临时变量,因此就算你在后台binding了一个tableData,beetl 也不能识别。在渲染ajax片段的时候会报变量tableData找不到。改正的办法只能是让tableData全局变量。

返回Json好还是返回html片段好?这个难以定论.

  • 从后台性能看,将模型序列化成json性能会比渲染模板性能更好,但是,json还需要前端重新解析生成最终html dom节点,这可能会延迟最终数据的现实效果。而返回的html片段就是已经生成好的dom

  • 从网络传入来看,json无疑更好的,html片段会有额外的html标记,css属性,以及有可能的js调用。传入流量有可能增加50%到100%。但是,对于web应用类,这些额外数据,并不算多。

  • 从开发效率来讲,返回html片段的开发效率更高一些,因为渲染在后台操作,可以随心所欲的用模板语言来渲染,来取得后台数据,完成复杂渲染,而json就比较困难,可以说所有的json lib都没有完美的解决办法。

  • 从用户体验上来讲,Beetl 采用ajax标记,混合了传统的模板渲染和ajax加载。用户进入页面即能看到数据,而经典的ajax json方式还需要异步加载,显示延迟。另外如果页面同时有多个ajax加载,则会对服务器造成很大的压力。

  • 关心服务器cpu消耗? 模板方式消耗更多的cpu,json方式则少点。但是俩者差距并不大。而且更多的web网站面临的情况是有富余的服务器CPU能力

  • 关心客户端CPU消耗? 过多的js无疑是客户端运行慢的主要原因。如果采用经典的json方式,返回的json数据必然还需要经过js的计算和渲染。会影响客户机器cpu。

4.13. 在页面输出错误提示信息

2.2.3版本以后,新增加org.beetl.ext.web.WebErrorHandler,可以在web开发的时候在页面输出提示信息,在产品模式下载后台输出提示信息(通过配置属性ESOURCE.autoCheck= true来认为是开发模式),仅仅需要配置如下:

1
ERROR_HANDLER = org.beetl.ext.web.WebErrorHandler
原文地址:https://www.cnblogs.com/zhanghaiyang/p/7212774.html