记一次org.thymeleaf.exceptions.TemplateInputException异常的处理过程


我们使用SpringBoot+thymeleaf创建新项目,一般目录如下:

 

当我们请求 /home/index,自然会请求到

 

但我们把返回的视图路径改了,或者写错了,如下图

 

那我们得到结果是什么呢?没错,是白页,也说得很清楚了,无法渲染模板,这个模板可能不存在。

 

我们看看报的错误信息。抛出了一个org.thymeleaf.exceptions.TemplateInputException,正是这个异常,折腾了我不少时间来折腾如何捕捉。(可能也是经验不足吧)

 

为什么说我要折腾这个异常呢,我们把正确的模板地址返回不就完事了吗。呃,如果我能把地址能完整地写下来,就不存在折腾这一事了。折腾的原因是这样的,我们的项目比较特殊,用户请求的逻辑写在SpringBoot这个项目上,而这些模板文件并不与项目共同存在同一个工程上,而是部署在

远程的服务器上,也就一个静态资源服务器。所以我对thymeleaf的配置进行了更改,让它映射的地址指向了远程服务器上。而用户请求的地址是分版本号的,用户输入什么版本号,我就得根据这个版本号去找到对应的目录,对应的文件,取出模板文件进行渲染。所以远程静态资源服务器上可以

自行地添加各个版本而不需要对SpringBoot这个项目进行部署。但如果用户输入的地址不对呢?那就是找不到这个模板文件了,找不到这个模板文件,我得给出一个友好的404或500页面,而不是冷冰冰的白色错误页面。

 

思路1:

既然报500,也有对应的异常信息,那我做个全局异常处理就可以吧。于是乎就使用@ControllerAdvice做了个全局异常处理的类,结果没进断点位置。

 

Controller Advice 字面上意思是控制器通知,加上@ExceptionHandler这个注解,即能捕捉到所有控制器请求后发生的异常,但我们跟踪代码得到,该

异常发生在模板引擎上,而非Controller上。所以在做这个全局异常处理时,无法截获到这个异常,所以这个方案走不通了。

思路2:

HandlerInterceptor,通过拦截器处理,我们知道HandlerInterceptor有三个方法,分别在请求前,请求后,渲染后处理的,各个方法的处理时间与节点如下:

 

看起来,貌似在afterCompletion上可以拿得到这个异常吧。于是乎,又做了一个自定义的 HandlerInterceptor。

 

 

但发现 response.sendRedirect("/error"); 并不能生效,不能正常地跳转。报的还是之前的错。

这里这个 response.sendRedirect("/error");不能生效是因为在渲染后,response已经把页面output出去了,再调用sendRedirect是无用的。也就是说这里只能知道之前发生过异常,并且把

Whitelabel Error Page 输出来了。那是不是意味着,我在这之前,找到一个入口点,这个入口点是在发生异常后,输出白页前,把输出白页这个逻辑改了,改成跳转,是不是可以了。带着这个

想法与疑问,于是乎有了下面的思路3。

思路3:

从异常信息的调用栈一步一步跟踪下去。

 

发现了这个自动配置的类:ErrorMvcAutoConfiguration,看到了下面有这么的一段代码

 

输出白页的内容是硬编码的,而负责这个输入的类名为:SpelView,这个类继承了View这个父类。而从下面的@ConditionalOnMissingBean这个注解

可以看出,这个白页错误类的输出并不是写死的,而是有条件地加载到Spring容器上,如果用户自己没定义过error这个Bean,就会加载这个SpelView作为

渲染失败的输出类,跟进去这个类,我们能清楚看到一个名叫render的方法,这里有三个参数,对我们最有用的是这个response,未输出,我们可以使用。

看到这个方法的最后一行,就知道,我们看到的白页,是从这里输出来的。

 

那也就是说,我们只要实现一个View的子类,并重写了render这个方法,把这个类注入到Spring容器上,是不是可以解决我们的问题呢。于是乎,我又建了一个类

 

以Bean的形式注入到Spring容器上

 

果然,在发生异常时,能进到这个render方法上了。接下来看能否正常地跳转就可以了。

 

代码执行到最后,能跳转到error错误页面了。thank god ! 这里甚至也带上了响应码,错误信息,可以根据model提供的数据进行进一步的逻辑判断。从而

做出更好的逻辑处理。

 

PS,在找error这个Bean类的时候,还发现一个事,就是我们如果把error.html放到 resource/templates 下,error这个bean就不会注入到spring容器上,

当发生异常时,直接会使用到error.html这个页面输出来,当然,那个status,error,这个属性还是会带到页面上,我们也可以用这个值来控制显示内容,

 

至于为什么加上这个error.html就用注入error这个bean,就没有深入研究了,留下大家。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM