com.jfinal.template.TemplateException: encode UTF8 error 3.3以上报错

jfinal 3.3到3.6遇到特殊字符会报错(),3.2以前就不会


报错信息如下

Template: "/inde.html". Line: 6

at com.jfinal.render.TemplateRender.render(TemplateRender.java:62)

at com.jfinal.core.ActionHandler.handle(ActionHandler.java:108)

at com.jfinal.ext.handler.ContextPathHandler.handle(ContextPathHandler.java:48)

at com.daoyou.niuniu.APPConfig$2.handle(APPConfig.java:209)

at com.jfinal.plugin.druid.DruidStatViewHandler.handle(DruidStatViewHandler.java:81)

at com.jfinal.core.JFinalFilter.doFilter(JFinalFilter.java:73)

at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)

at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)

at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)

at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:475)

at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)


评论区

JFinal

2019-02-25 18:48

Enjoy 模板引擎对 UTF-8 的 encoding 做过性能优化,某些偏门字符在 被编码为 UTF-8 时会出现异常,此时可以通过继承扩展 EncoderFactory 来解决编码异常


具体用法如下:

1:创建 MyEncoderFactory
public class MyEncoderFactory extends EncoderFactory {
public Encoder getEncoder() {
return new JdkEncoder(charset);
}
}

2:配置 enjoy 切换到自己的 MyEncoderFactory
public void configEngine(Engine me) {
me.getEngineConfig().setEncoderFactory(new MyEncoderFactory());
}

年少

2019-02-26 12:32

使用上面的方法后会报另一个错 java.nio.charset.MalformedInputException: Input length = 1

后面发现主要是字符串截取问题,表情符号会占用两个字符,String 截取时(String .substring(start,end),如果刚好截到表情符号的一个字符,而另一个字符没有截取进来,Enjoy会抛出,java本身的话不抛出,会直接用一个?代替。

修改字符串截取方式后,问题解决
private static String subByUtf32(String s, int reqNum) throws UnsupportedEncodingException{
byte[] bts = s.getBytes("utf-32");
int index = reqNum * 4;
index = index > bts.length ? bts.length : index;
return new String(bts, 0, index, "utf-32");
}

JFinal

2019-02-26 13:28

@年少 字符串截取代码是在什么地方?

enjoy 解析时对于模板内容也是可以指定 encoding 的,同样也是在 configEngine 中配置:
engine.setEncoding("utf-32");

如果你不配置,默认就是 UTF-8。建议你通过上面的配置来代替你原有的 subByUtf32 的办法,记得搞定后来反馈

补充一下,前面的 MyEncoderFactory 方案是解决渲染转换 byte[] 用的 encoding,现在的 engine.setEncoding 同时也控制了模板解析时用的 encoding

年少

2019-02-27 18:46

是我字符串截取问题,比如“.......”,如果substring(0,5)就会截取到最后一个表情字符的一半,这样UTF-8编码会报错,只是java本身遇到这种情况不抛出来直接用一个“?”代替打印出来变成“....?”,我用subByUtf32上面那个方法不是改变编码方式,只是为了防止截取到一半的事情发生,会自动截取完整表情字符。

“.......”是表情字符,这里不能用表情字符,用的话就发送不了(不知道这个问题跟我的问题是不是一样的,存在表情字符只获取一半的问题),

JFinal

2019-02-27 20:16

@年少 在 jfinal 这端不需要动什么地方吧?

年少

2019-02-28 17:35

@JFinal 不需要改动

阿龙

2019-07-11 18:13

今天升级版本遇到同样的问题,貌似也是截取的问题。但是我是使用escape转义html遇到的,打算通过后台来转义了。

JFinal

2019-07-12 10:29

@阿龙 用上我第一条回复的代码即可解决,那个方案是使用 JDK 默认的 encoding 算法,虽然性能不及 jfinal 优化过的 Utf8Encoder ,但兼容性应该要好很多

阿龙

2019-07-12 14:22

@JFinal 波总,开始也是encode异常,使用jdk默认算法后解决。但是会报字符串截取错误(Input length = 1),目前我暂时把escape去掉就好了。

JFinal

2019-07-12 14:58

@阿龙 去掉 escape 是啥意思,再多给些信息量,好让 jfinal 解决这个问题,避免后来的同学碰到这个问题

此外, 如果你指的是 #escape 指令的话, 用一下 jfinal 4.3 版本, 这个版本改进过 #escape 指令

阿龙

2019-07-12 15:26

@JFinal 是这样的!我一直没升级,很早用的是3点几的版本。
昨天升级之后个别使用到#escape的行就开始报错了(com.jfinal.template.TemplateException: encode UTF8),不是所有行。
然后根据第一条所回复修改之后,变成了这样
com.jfinal.template.TemplateException: Encode error: Input length = 1
Template: "/topic/index.html". Line: 43
at com.jfinal.template.ext.directive.EscapeDirective.exec(EscapeDirective.java:61)
at com.jfinal.template.stat.ast.StatList.exec(StatList.java:68)
at com.jfinal.template.stat.ast.If.exec(If.java:54)
at com.jfinal.template.stat.ast.StatList.exec(StatList.java:68)
at com.jfinal.template.stat.ast.For.forIterator(For.java:74)
at com.jfinal.template.stat.ast.For.exec(For.java:53)
at com.jfinal.template.stat.ast.StatList.exec(StatList.java:68)
at com.jfinal.template.Template.render(Template.java:58)
at com.jfinal.render.TemplateRender.render(TemplateRender.java:61)
at com.jfinal.core.ActionHandler.handle(ActionHandler.java:107)
at com.jfinal.core.JFinalFilter.doFilter(JFinalFilter.java:89)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
我目前暂时不使用#escape了,就好了。

JFinal

2019-07-12 15:48

@阿龙 能不能定位到具体是哪一个字符在 escape 的时候出的问题,帮我测试一下:
Tempate t = engine.getTemplateByString("#escape('出问题的字符')");
String ret = t.renderToString(null);
System.out.println(ret);

关键是给出 #escape 中出问题的字符

阿龙

2019-07-12 16:31

@JFinal 符号是一个笑脸。
我刚看了下,好奇怪。
同样的代码我本地跑的时候这个笑脸就被正常转义成了?,服务器上转义不过去,就报错了。

JFinal

2019-07-12 17:03

@阿龙 在 qq 群里面发给我

阿龙

2019-07-15 13:09

@JFinal 波总,最后是啥问题?分享下,我们也学习学习!

JFinal

2019-07-16 22:25

@阿龙 首先是 enjoy 自带的 Utf8Encoder 不支持 utf8mb4 , 通过 配置 JdkEncoder 支持 utf8mb4 后, 但 #escape 指令不支持对 utf8mb4 进行转义

阿龙

2019-07-17 14:11

@JFinal 那处理的思路是不是优化enjoy,来支持utf8mb4?

JFinal

2019-07-17 15:40

@阿龙 通过改写 Utf8Encoder 即可, 相当于是做一个新的 Utf8mb4Encoder,这部分接口是开放的,你也可以自己写一个, 然后自己通过我前面回复中的方法让其生效

jfinal 中绝大部分功能都是开放可扩展的

阿龙

2019-07-18 15:41

@JFinal 偷个懒,暂时我把emoji表情给过滤掉了

JFinal

2019-08-05 11:13

@阿龙 @年少 升级到 jfinal 4.3 试一下,这一版本改进过 #escape 指令,并且已发布到 maven 中心库。

注意要先根据第一条回复中的办法用上 JdkEncoder

jfinal 4.4 已经添加了一个 Engine.setToJdkEncoderFactory(), 当前的版本暂时用我的第一条回复中的扩展方式:
https://gitee.com/jfinal/jfinal/blob/master/src/main/java/com/jfinal/template/io/JdkEncoderFactory.java

热门反馈

扫码入社