我們使用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,就沒有深入研究了,留下大家。