jsp改造之sitemesh注意事项

背景

  1. 现在各种现代化的浏览器确实惯坏了开发者 智能纠错 无论是忘记关闭标签甚至重复等等都有可能被chrome这些浏览器智能纠错===》chrome会合并多个body
  2. 使用jsp开发的小伙伴新开一个新的组件页面时可能从原先的页面拷贝而来 那么里面包含了如下一些
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <base href="<%=basePath%>">
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
 
    <meta http-equiv="description" content="首页">
    <jsp:include page="${path}/WEB-INF/page/common/reference.jsp"/>
    <script type="text/javascript" src="${staticPath}/js/index/index.js"></script>
    <script type="text/javascript" src="${staticPath}/js/index/scroll.js"></script>
    <script type="text/javascript" src="<%=path%>/plugins/slider/slider.js"></script>
    <script type="text/javascript">
        var sessionIdOrg = "${sessionScope.organization.pkId}";
    </script>
</head>
<body class="mainBody">
</body>
</html

这样造成一个页面从jsp处理完毕后输出的响应流会包含多个

可以参考

一个html文档可以有两组标签吗?

问题

多个body对于chrome来说会做自动忽略,那么对于我们使用sitemesh呢???

这个问题自然是存在的 否则也就不会有这篇博客了!

sitemesh的工作原理很单纯 使用filter对响应进行拦截,如果返回的是html那么判断是否需要进行装饰。如果需要找到合适的装饰模板进行装饰 并返回新的响应!

代码

对于sitemesh 默认来说会注入规则CoreHtmlTagRuleBundle

public void install(State defaultState, ContentProperty contentProperty, SiteMeshContext siteMeshContext) {
    // Core rules for SiteMesh to be functional.
    defaultState.addRule("head", new ExportTagToContentRule(siteMeshContext, contentProperty.getChild("head"), false));
    defaultState.addRule("title", new ExportTagToContentRule(siteMeshContext, contentProperty.getChild("title"), false));
    defaultState.addRule("body", new ExportTagToContentRule(siteMeshContext, contentProperty.getChild("body"), false));
    defaultState.addRule("meta", new MetaTagRule(contentProperty.getChild("meta")));
 
    // Ensure that while in <xml> tag, none of the other rules kick in.
    // For example <xml><book><title>hello</title></book></xml> should not affect the title of the page.
    defaultState.addRule("xml", new StateTransitionRule(new State()));
}
 
public void cleanUp(State defaultState, ContentProperty contentProperty, SiteMeshContext siteMeshContext) {
    // In the event that no <body> tag was captured, use the default buffer contents instead
    // (i.e. the whole document, except anything that was written to other buffers).
    if (!contentProperty.getChild("body").hasValue()) {
        contentProperty.getChild("body").setValue(contentProperty.getValue());
    }
}

从这边可以看出body使用的规则是ExportTagToContentRule

我们细看一下ExportTagToContentRulede的源码

/**
 * Exports the contents of a match tag to property of the passed in {@link ContentProperty}.
 *
 * Additionally, if this tag has attributes, they will be written as child properties.
 *
 * <h3>Example</h3>
 *
 * <pre>
 * // Java
 * myState.addRule("foo", new ExportTagToContentRule(content, "bar");
 *
 * // Input
 * <foo x=1 b=2>hello&lt/foo>
 *
 * // Exported properties of Content
 * bar=hello
 * bar.x=1
 * bar.b=2
 * </pre>
 *
 * @author Joe Walnes
 */
public class ExportTagToContentRule extends BasicBlockRule {
 
    private final ContentProperty targetProperty;
    private final boolean includeInContent;
    private final SiteMeshContext context;
;
    /**
     * @param targetProperty   ContentProperty to export tag contents to.
     * @param includeInContent Whether the tag should be included in the content (if false, it will be stripped
     *                         from the current ContentProperty that is being written to.
     * @see ExportTagToContentRule
     */
    public ExportTagToContentRule(SiteMeshContext context, ContentProperty targetProperty, boolean includeInContent) {
        this.targetProperty = targetProperty;
        this.includeInContent = includeInContent;
        this.context = context;
    }
 
    @Override
    protected Object processStart(Tag tag) throws IOException {
        // Some terminology:
        // Given a tag: '<foo>hello</foo>'
        // INNER contents refers to 'hello'
        // OUTER contents refers to '<foo>hello</foo>'
         
        Tag t = tag;
 
        // Export all attributes of the opening tag as child nodes on the target ContentProperty.
        for (int i = 0; i < t.getAttributeCount(); i++) {
            // attributes of tags using this rule doesn't expand sitemesh:write
            // https://github.com/sitemesh/sitemesh3/issues/23
            String value = t.getAttributeValue(i);
             
            // only if there might be another tag inside the attribute
            if(value != null && (value.indexOf('<') < value.indexOf('>'))){
                StringBuilder sb = new StringBuilder();
                context.getContentProcessor().build(CharBuffer.wrap(value), context).getData().writeValueTo(sb);
                value = sb.toString();
                 
                if(!(t instanceof CustomTag)){
                    t = new CustomTag(t);
                }
                   
                CustomTag custom = (CustomTag) t;
                custom.setAttributeValue(i, value);
                 
            }
                 
            targetProperty.getChild(t.getAttributeName(i)).setValue(value);
        }
 
        // Push a buffer for the OUTER contents.
        if (!includeInContent) {
            // If the tag should NOT be included in the contents, we use a data-only buffer,
            // which means that although the contents won't be written
            // back to the ContentProperty, they will be available in the main Content data.
            // See Content.createDataOnlyBuffer()
            tagProcessorContext.pushBuffer(targetProperty.getOwningContent().createDataOnlyBuffer());
        } else {
            tagProcessorContext.pushBuffer();
        }
 
        // Write opening tag to OUTER buffer.
        t.writeTo(tagProcessorContext.currentBuffer());
 
        // Push a new buffer for storing the INNER contents.
        tagProcessorContext.pushBuffer();
        return null;
    }
 
    @Override
    protected void processEnd(Tag tag, Object data) throws IOException {
        // Get INNER content, and pop the buffer for INNER contents.
        CharSequence innerContent = tagProcessorContext.currentBufferContents();
        tagProcessorContext.popBuffer();
 
        // Write the INNER content and closing tag, to OUTER buffer and pop it.
        tagProcessorContext.currentBuffer().append(innerContent);
        if (tag.getType() != Tag.Type.EMPTY) { // if the tag is empty we have already written it in processStart().
            tag.writeTo(tagProcessorContext.currentBuffer());
        }
        CharSequence outerContent = tagProcessorContext.currentBufferContents();
        tagProcessorContext.popBuffer();
 
        // Write the OUTER contents to the current buffer, which is now the buffer before the
        // tag was processed. Note that if !includeInContent, this buffer will not be written
        // to the ContentProperty (though it will be available in Content.getData()).
        // See comment in processStart().
        tagProcessorContext.currentBuffer().append(outerContent);
 
        // Export the tag's inner contents to
        if (!targetProperty.hasValue()) {
            targetProperty.setValue(innerContent);
        }
    }
}

可以看到export的最后做了判断为

if (!targetProperty.hasValue()) {
           targetProperty.setValue(innerContent);
       }

可以看到当页面上存在多个body标签时sitemesh只会输出第一个body里面的内容!!!

措施

虽然chrome如此智能,虽然我们也不用考虑对其他老旧慢浏览器的支持。但是对于一个合格的后端开发来说

html的输出格式还是要注意的。因此考虑将多余的body删除【大量引入popup的jsp的都包含了一整套的html body】

那么需要清除一些jsp中的多余的body

建议

新建的jsp如果不是页面【是指将会被include其他的jsp中】那么而建议用_打头

比如一个关于左侧导航的可以考虑使用_left.jsp

以_打头的jsp中不允许出现html body 等标签

小问题

有小伙伴发现sitemesh的script提取 也有同样的问题么?^_^

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏算法修养

PAT 甲级 1025 PAT Ranking

1025. PAT Ranking (25) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题...

30710
来自专栏技术专栏

logback日志写入kafka遇到的那些坑

这两天在学习storm实时流的时候需要将logback日志写入kafka,这期间遇到了很多坑,这里把遇到的坑和解决的问题记录一下,和大家共勉

1.2K3
来自专栏一枝花算不算浪漫

SpringBoot自定义序列化的使用方式--WebMvcConfigurationSupport

1471
来自专栏一个会写诗的程序员的博客

《Springboot极简教程》 第11章 Springboot集成mongodb开发小结

本章我们通过SpringBoot集成mongodb,Java,Kotlin开发一个极简社区文章博客系统。

994
来自专栏Java与Android技术栈

使用 Kotlin + Spring Boot 进行后端开发Kotlin示例一:结合 Redis 进行数据存储和查询示例二:结合 RxJava 模拟顺序、并发地执行任务总结

Kotlin 是一个基于 JVM 的编程语言,它的简洁、便利早已不言而喻。Kotlin 能够胜任 Java 做的所有事。目前,我们公司 C 端 的 Androi...

1303
来自专栏菩提树下的杨过

silverlight:ListBox中如何取得DateTemplate/ItemsPanelTemplate中的命名控件?

Xaml如下: <UserControl x:Class="ToolsTest.Test"     xmlns="http://schemas.microsof...

1915
来自专栏Android源码框架分析

获取Android设备DeviceId与反Xposed Hook技术

APP开发中常需要获取设备的DeviceId,以应对刷单,目前常用的几个设备识别码主要有IMEI(国际移动设备身份码 International Mobile ...

3312
来自专栏py+selenium

pip install xxxx报错(一大堆红色exception)【解决】

  File "/usr/lib/python2.7/dist-packages/pip/basecommand.py", line 215, in main

2861
来自专栏石奈子的Java之路

原 荐 SpringBoot 2.0 系列0

3823
来自专栏后端之路

数据校验之Spring和Hibernate validate

目前系统中使用校验的地方比较多,前端校验&后端校验是绕不开的两个话题。 通常来说对于开发来说:后端校验是必备,前端校验是可选 1.前端验证可以没有,但后端验证必...

2455

扫码关注云+社区