ERROR 15028 — [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: “class path resource [templates/admin/user_list.html]”)] with root cause
报错原因
2023-09-15 15:47:12.799 ERROR 15028 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/admin/user_list.html]")] with root cause
org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "
{checkbox: true, fixed: true},
{field:'userId',title: 'ID', sort:true},
{field: 'userName',title: '用户名'},
{field: 'userPwd',title: '密码'},
{field: 'userEmail',title: '邮箱'},
{field: 'userRealName',title: '真实姓名'},
{field: 'userPhone',title: '手机号码'},
{field: 'userRegisterTime',title: '注册时间'},
{field: 'userUpdateTime',title: '更新时间'}
" (template: "admin/user_list" - line 142, col 28)
at org.thymeleaf.standard.expression.StandardExpressionParser.parseExpression(StandardExpressionParser.java:131) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.standard.expression.StandardExpressionParser.parseExpression(StandardExpressionParser.java:62) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.standard.expression.StandardExpressionParser.parseExpression(StandardExpressionParser.java:44) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.engine.EngineEventUtils.parseAttributeExpression(EngineEventUtils.java:220) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.engine.EngineEventUtils.computeAttributeExpression(EngineEventUtils.java:207) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:125) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.engine.TemplateHandlerAdapterMarkupHandler.handleOpenElementEnd(TemplateHandlerAdapterMarkupHandler.java:304) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler$InlineMarkupAdapterPreProcessorHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:278) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.standard.inline.OutputExpressionInlinePreProcessorHandler.performInlining(OutputExpressionInlinePreProcessorHandler.java:440) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.standard.inline.OutputExpressionInlinePreProcessorHandler.handleText(OutputExpressionInlinePreProcessorHandler.java:146) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler.handleText(InlinedOutputExpressionMarkupHandler.java:80) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.attoparser.HtmlMarkupHandler.handleText(HtmlMarkupHandler.java:208) ~[attoparser-2.0.5.RELEASE.jar:2.0.5.RELEASE]
at org.attoparser.AbstractChainedMarkupHandler.handleText(AbstractChainedMarkupHandler.java:203) ~[attoparser-2.0.5.RELEASE.jar:2.0.5.RELEASE]
at org.attoparser.MarkupParser.parseBuffer(MarkupParser.java:557) ~[attoparser-2.0.5.RELEASE.jar:2.0.5.RELEASE]
at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:301) ~[attoparser-2.0.5.RELEASE.jar:2.0.5.RELEASE]
at org.attoparser.MarkupParser.parse(MarkupParser.java:257) ~[attoparser-2.0.5.RELEASE.jar:2.0.5.RELEASE]
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parseStandalone(AbstractMarkupTemplateParser.java:100) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:666) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072) ~[thymeleaf-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:362) ~[thymeleaf-spring5-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:189) ~[thymeleaf-spring5-3.0.11.RELEASE.jar:3.0.11.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1372) ~[spring-webmvc-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1118) ~[spring-webmvc-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1057) ~[spring-webmvc-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123) ~[druid-1.1.10.jar:1.1.10]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:94) ~[spring-web-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.1.11.RELEASE.jar:5.1.11.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579) [tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.27.jar:9.0.27]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_371]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_371]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.27.jar:9.0.27]
at java.lang.Thread.run(Thread.java:750) [na:1.8.0_371]
2023-09-15 15:47:12.812 ERROR 15028 --- [nio-8080-exec-1] s.e.ErrorMvcAutoConfiguration$StaticView : Cannot render error page for request [/user/findAll] and exception [An error happened during template parsing (template: "class path resource [templates/admin/user_list.html]")] as the response has already been committed. As a result, the response may have the wrong status code.
我的user_list.html下Layui中的JS代码
<script type="text/javascript" >
// 加载table和form模块
layui.use(['table','form'],function () {
let table = layui.table;
let form = layui.form;
//渲染表格
table.render({
elem: '#listTable',
url:'/user/findAll',
cols:[[
{checkbox: true, fixed: true},
{field:'userId',title: 'ID', sort:true},
{field: 'userName',title: '用户名'},
{field: 'userPwd',title: '密码'},
{field: 'userEmail',title: '邮箱'},
{field: 'userRealName',title: '真实姓名'},
{field: 'userPhone',title: '手机号码'},
{field: 'userRegisterTime',title: '注册时间'},
{field: 'userUpdateTime',title: '更新时间'}
]],
page: true, // 开启分页
limit: 10, // 每页显示的数量
limits: [10, 20, 30], // 每页可选择的数量
height: 'full-200', // 表格高度
checkbox: true, // 显示复选框
fixed: true // 固定表头和列
});
//监听搜索表单提交事件
form.on('submit(searchBtn)',function (data) {
// var name = data.field.searchInputName;
// var email = data.field.searchInputEmail;
// var phone = data.field.searchInputPhone;
let searchByName = $('#searchInputName').val();
let searchByEmail = $('#searchInputEmail').val();
let searchByPhone = $('#searchInputPhone').val();
// 检查关键字是否为空
if (searchByName.trim() === '' && searchByEmail.trim() === '' && searchByPhone.trim() === ''){
layer.msg("请输入关键字");
return;
}
// 发送查询请求
table.reload('userTable',{
where:{
username:searchByName,
userEmail:searchByEmail,
userPhone:searchByPhone
}
});
return false;
});
});
</script>
问题解决:在script标签中添加
th:inline="none"
<script type="text/javascript" th:inline="none"></script>
问题二
- 在实现添加用户操作时出现的错误
检查了添加用户的弹出层中问题所在的一个小细节:括号中的name未加单引号
我的user_list.js所有代码
// 加载table和form模块
layui.use(['table','form','layer','jquery'],function () {
let table = layui.table;
let form = layui.form;
let layer = layui.layer;
let $ = layui.jquery;
// 渲染表格
table.render({
elem: '#userTable',
url: '/user/find',
page:true,
limit:10,
limits: [10, 20, 30],
cellMinWidth:60,
cols:[[
{type: 'checkbox', fixed: true}, // 单选框
{field:'userId', title: 'ID', width:80, sort: true, fixed: true},
{field:'username',title: '用户名'},
{field:'userPwd',title: '密码'},
{field:'userEmail',title: '邮箱'},
{field:'userRealName',title: '真实姓名'},
{field:'userPhone',title: '手机号'},
{field:'userRegisterTime',title: '注册时间'},
{field:'userUpdateTime',title: '修改时间'},
{field: 'operation', title: '操作', toolbar:'#operation-bar',
fixed: 'right',
width: 134,
minWidth: 125,
}
]],
toolbar: '#toolbar-header', //开启头部工具栏,并为其绑定左侧模板
defaultToolbar: ['filter', 'exports', 'print']//头部工具栏右侧
});
// 添加用户
table.on('toolbar(userTable)', function(obj){
if(obj.event === 'add'){
layer.open({
type:2,
title:'添加',
content:['/user/add','yes'],
area: ['680px', '520px'],
btn: ['确认添加', '取消'],
maxmin:true,
btnAlign: 'c',
yes:function (index, layero) {
// 获取iframe窗口
var iframeWin = window[layero.find('iframe')[0][name]];
// 获取表单中的数据
var username = iframeWin.$('#username').val();
var userPwd = iframeWin.$('#userPwd').val();
var userEmail = iframeWin.$('#userEmail').val();
var userRealName = iframeWin.$('#userRealName').val();
var userPhone = iframeWin.$('#userPhone').val();
// 发送请求
$.ajax({
url: '/user/add',
type: 'POST',
data: {
username: username,
userPwd: userPwd,
userEmail: userEmail,
userRealName: userRealName,
userPhone: userPhone
},
success: function(res){
if(res.code === 0){
layer.msg('添加成功');
table.reload('userTable');
layer.close(index);
}else{
layer.msg('添加失败');
}
},
error:function (data) {
console.log(data);
}
});
}
});
}
});
// 监听表格行工具条事件
table.on('tool(userTable)', function(obj){
let data = obj.data; // 当前行数据
let layEvent = obj.event; // 获取点击的事件名
let userId = data.userId;
let $ = layui.$;
if(layEvent === 'edit') {
// 编辑操作
layer.open({
type: 2,
title: '编辑',
content: ['/user/'+ userId + '/edit','yes'],
area: ['680px', '520px'],
btn: ['保存', '取消'],
btnAlign: 'c',
yes: function(index, layero){
// 获取 iframe 的窗口对象
let iframeWin = window[layero.find('iframe')[0]['name']];
// 获取表单中的数据
let username = iframeWin.$('#username').val();
let userPwd = iframeWin.$('#userPwd').val();
let userEmail = iframeWin.$('#userEmail').val();
let userRealName = iframeWin.$('#userRealName').val();
let userPhone = iframeWin.$('#userPhone').val();
//提交表单
$.ajax({
url: '/user/' + userId + '/update',
type: 'post',
data:{
'username':username,
'userPwd':userPwd,
'userEmail':userEmail,
'userRealName':userRealName,
'userPhone':userPhone
},
// data: $('#editForm').serialize(),
success: function(res){
layer.msg('保存成功');
// 更新成功后,刷新表格
table.reload('userTable');
layer.close(index);
},
error: function(error){
layer.msg('保存失败'+error);
}
});
},
btn2: function(index, layero){
// 取消操作
layer.close(index);
}
});
} else if(layEvent === 'delete') {
// 删除操作
layer.confirm('确定删除此用户吗?',function (index) {
$.ajax({
type: 'get',
url: '/user/' + userId+'/delete',
success: function(res){
layer.msg('删除成功');
table.reload('userTable');
},
error: function(){
layer.msg('删除失败');
}
});
layer.close(index);
})
}
});
//监听搜索表单提交事件
form.on('submit(searchBtn)',function (data) {
console.log(data);
let userName = data.field.username;
let userEmail = data.field.userEmail;
let userPhone = data.field.userPhone;
let searchByName = $('#searchInputName').val();
let searchByEmail = $('#searchInputEmail').val();
let searchByPhone = $('#searchInputPhone').val();
// 检查关键字是否为空
if (searchByName.trim() === '' && searchByEmail.trim() === '' && searchByPhone.trim() === ''){
layer.msg("请输入关键字");
return;
}
// 发送查询请求执行查询
table.reload('userTable',{
where:{
username:userName || undefined,
userEmail:userEmail || undefined,
userPhone:userPhone || undefined,
// 如果关键字为空则传递undefined给后端接口
},
page: {
curr: 1 // 搜索后跳转至第1页
}
});
return false;// 阻止表单默认提交
});
});
问题解决:
//未修改前
let iframeWin = window[layero.find('iframe')[0][name]];
//修改后
let iframeWin = window[layero.find('iframe')[0]['name']];
Layui中的table.on()
table.on('tool(userTable)')
与table.on('toolbar(userTable)')
的区别
- table.on(‘tool(userTable)’):
这个事件监听函数用于监听表格中的工具条点击事件。当表格中的工具条(通常是每一行数据后面的操作按钮)被点击时,会触发此事件。在该事件处理函数内部,可以根据不同的工具条类型进行相应的操作,例如编辑、删除等。
示例代码:
table.on('tool(userTable)', function(obj){
var data = obj.data; // 获取当前行的数据
var layEvent = obj.event; // 获取触发的工具条类型
if(layEvent === 'edit'){
// 编辑操作
} else if(layEvent === 'delete'){
// 删除操作
} else if(layEvent === 'detail'){
// 查看详情操作
}
// ...
});
- table.on(‘toolbar(userTable)’):
这个事件监听函数用于监听表格的工具栏点击事件。通常,Layui的表格插件会在表格的顶部或底部添加一个工具栏,用于批量操作或其他自定义操作。当工具栏按钮被点击时,会触发此事件。
示例代码:
table.on('toolbar(userTable)', function(obj){
var layEvent = obj.event; // 获取触发的工具栏按钮类型
if(layEvent === 'add'){
// 添加操作
} else if(layEvent === 'batchDelete'){
// 批量删除操作
} else if(layEvent === 'export'){
// 导出操作
}
// ...
});
总结:
table.on(‘tool(userTable)’)
用于监听行工具条的点击事件。
table.on(‘toolbar(userTable)’)
用于监听表格工具栏的点击事件。
两者主要区别在于监听的事件类型不同,以及事件处理函数中对应的操作目标(行数据还是整个表格)。根据具体的需求,使用相应的事件监听函数来处理对应的事件。