SpringBoot+Thymeleaf项目控制台报错 org.thymeleaf.exceptions.TemplateInputException An error happened during

一、问题描述

在我写一个社区项目的过程中,运行 SpringBoot 后访问 index.html 页面,浏览器页面出现 500 错误,且 IDEA 控制台出现了如下错误信息:

2021-05-03 20:31:40.083 ERROR 9080 --- [nio-8080-exec-1] org.thymeleaf.TemplateEngine             : [THYMELEAF][http-nio-8080-exec-1] Exception processing template "/index": An error happened during template parsing (template: "class path resource [templates//index.html]")

org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates//index.html]")
......

此时我的项目环境是 SpringBoot + Thymeleaf。

二、问题分析

下面部分都是我分析的过程,如果读者不感兴趣的话,可以直接跳到第三部分。

出错时的控制层对应方法如下:

// 获取首页
@GetMapping(path = "/index")
public String getIndexPage(Model model){
     
    // 1. 查询首页帖子
    List<DiscussPost> list = discussPostService.selectDiscussPosts(0, 0, 10);
    // 2.将每个用户信息以及讨论帖存放到一个 map 集合中
    // 然后把 map 集合存放到一个 list 集合中,因为首页肯定有很多用户以及讨论帖
    List<Map<String, Object>> discussPosts = new ArrayList<>();
    if (list != null){
     
        for (DiscussPost post : list){
     
            // 将每个用户的讨论帖存放到一个 map 集合中。user 为 key,讨论帖为 val
            Map<String, Object> map = new HashMap<>();
            // 保存讨论帖
            map.put("post", post);
            // 保存用户信息
            User user = userService.findUserById(post.getUserId());
            map.put("user", user);

            // 加入到 list 集合中
            discussPosts.add(map);
        }
    }
    model.addAttribute("discussPosts", discussPosts);
    return "/index.html";
}

刚开始出现这个问题的时候,我看到报错内容有 templates//index.html,我首先想到的是前端页面路径问题。我们知道,对于 SpringMVC 来说,解析路径的配置信息在 WebMvcAutoConfiguration 类中,那么对于 thymeleaf 模板引擎,对应的配置类自然应该是 ThymeleafAutoConfiguration,在此类中有一个静态内部类 DefaultTemplateResolverConfiguration,从名字就可以看出来,这个类用于记录模板解析的配置信息,在这个类中有如下方法:

@Bean
SpringResourceTemplateResolver defaultTemplateResolver() {
     
    SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
    resolver.setApplicationContext(this.applicationContext);
    // 设置前缀
    resolver.setPrefix(this.properties.getPrefix());
    // 设置后缀
    resolver.setSuffix(this.properties.getSuffix());
    resolver.setTemplateMode(this.properties.getMode());
    if (this.properties.getEncoding() != null) {
     
        resolver.setCharacterEncoding(this.properties.getEncoding().name());
    }
    resolver.setCacheable(this.properties.isCache());
    Integer order = this.properties.getTemplateResolverOrder();
    if (order != null) {
     
        resolver.setOrder(order);
    }
    resolver.setCheckExistence(this.properties.isCheckTemplate());
    return resolver;
}

在这个方法中,我们可以看到为我们的返回值设置了路径前后缀,而默认的前后缀如下:

public static final String DEFAULT_PREFIX = "classpath:/templates/";

public static final String DEFAULT_SUFFIX = ".html";

到这里貌似就是 “柳暗花明” 了,是不是因为返回值是/index.html,而不是 index 引发的路径错误问题?

很遗憾,答案并不是这样。

再回头看看上面的 defaultTemplateResolver() 方法的第 8 行:

resolver.setSuffix(this.properties.getSuffix());

其中 setSuffix() 函数具体如下:

/**
 * 

* Sets a new (optional) suffix to be added to all template names in order * to convert template names into resource names. *

*

* Note that this suffix may not be applied to the template name if the template name * already ends in a known file name suffix: {@code .html}, {@code .htm}, {@code .xhtml}, * {@code .xml}, {@code .js}, {@code .json}, * {@code .css}, {@code .rss}, {@code .atom}, {@code .txt}. If this behaviour needs to be overridden so * that suffix is always applied, the {@link #setForceSuffix(boolean)} will need to be set. *

* * @param suffix the suffix to be set. */
public final void setSuffix(final String suffix) { this.suffix = suffix; }

在第 8 行和第 9 行的注释中已经明确说了:如果模板名称(也就是控制层方法返回值)中以 .html 为后缀结尾,则这个后缀不会生效。

也就是说,控制层方法直接返回 index.html 也不会出错,因为不会对其进行后缀拼接。至于在 index.html 前面加上一个 / 变成了 /index.html,也并不会影响路径访问。不信的话可以在浏览器中输入 https://www.baidu.com//index.html 也是可以照样访问百度的,应该是浏览器对分隔符做了处理。

至此,虽然没有排除出问题所在,但是起码学到了一点:在控制层方法中,返回的模板名称可以有文件后缀,也可以有 / 前缀。即如果要跳转到 index.html 页面,返回值可以是 /index.html,也可以是 index.html,也可以是 index

三、问题解决

经过查阅一些资料,算是找到了问题的根源。

一般出现这种错误,首先考虑的应该是前端页面出了一些问题,常见的问题可以分为两种。

  1. 第一种:在 html 文件头部重复引入 xmlns

    比如下面这种,在引入 thymeleaf 的同时还引入了别的 xmlns,这种情况下会出现上述错误。

    <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:https="http://www.w3.org/1999/xhtml">
    

    解决方案是只保留 thymeleaf,正确写法如下:

    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    
  2. 第二种:语法出错。

    比如下面这种:

    <a href="#">
        <img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像">
    a>
    

    我这里使用的是 map.user.headerUrl,实际上我在 POJO 对象中写的属性域是 headUrl,这里完全是粗心的问题,将其修改过来即可。大家如果排除了第一种可能性的话,一定要好好检查是不是自己的 thymeleaf 语法用错了或者说是对应的属性写错了。

修改完毕之后,重启部署项目,便可以成功访问页面。

你可能感兴趣的:(坑多多,java,thymeleaf,springboot,web)