javascript
SpringBoot Whitelabel Error Page的根本原因,三种解决方案以及其特点
原文地址:https://www.jianshu.com/p/b06584591086
0、簡述
在學習這個學習筆記之前最好能夠對spring mvc以及Tomcat有些了解,這樣理解起來更加方便,如果需要知道最直接的解決方案,拖到最底部看樣例代碼即可。
介紹了springboot的白頁出現的真正原因,主要是沒有合適的匹配情況出現404情況,然后跳轉到系統默認的第一個ErrorPage,也就是白頁內容上,然后根據其特定分別從三個角度,1、攔截器,2、新ErrorPage,3、自定義/error路由 去解決該問題,并且介紹各自方法的優缺點,其中還有介紹到循環頁面錯誤的本質原因等情況
1、Whitelabel Error Page 白頁
什么叫Whitelabel Error Page(也叫白頁),就是SpringBoot中HTTP請求出現異常的說明頁,如下圖
image
白頁內容會展示狀態碼、path、以及錯誤原因等情況,但是真正發布在線上生成環境一般不允許出現這樣的情況,更多的是自定義的404頁面或者500頁面等。
那么現在我們就來了解下什么情況會產生白頁的情況,以及如何解決這種情況。我們就以404的情況去了解其原因。
直接來到DispatcherServlet類的protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception方法,其中包含的代碼片段
mappedHandler = getHandler(processedRequest); // 找到合適的請求處理器 if (mappedHandler == null || mappedHandler.getHandler() == null) { // 原則上如果沒有找到則會進入到這里,并且設置response的狀態碼為404 // 但是經過調試并沒有進入到這里 noHandlerFound(processedRequest, response); return; }在getHandler方法中會遍歷當前web容器中的HandlerMapping,找出合適的處理器Handler
image
image
由上圖可以很明顯的知道便利出的當前Handler是SimpleUrlHandlerMapping,因為其中的url中包含了/**,所有的url都可以被匹配出,不會進入到后面的noHandlerFound中,適配處理器HandlerAdapter是HttpRequestHandlerAdapter實例化的對象
在mv = ha.handle(processedRequest, response, mappedHandler.getHandler())中沒法找到對應的resource,設置response的狀態碼為404,具體可看ResourceHttpRequestHandler類的handleRequest方法
現在就相當于該請求設置了狀態碼為404,其他并沒有做什么,mv也是為null的
這時候需要回到Tomcat的調用流程內,如果對Tomcat的調用流程請求的同學應該知道,Tomcat在連接器收到Socket套接字請求包裝成為request、response等信息交由Engine->Host 等組件一層一層傳遞,再由各個組件的Pipeline管道接收,后續各自的Valve(閥門)一層一層的過濾處理。
這個時候來到StandardHostValve類的private void status(Request request, Response response)方法
private void status(Request request, Response response) { int statusCode = response.getStatus(); // 查看當前狀態碼,當前樣例是404 // 獲取當前上下文 Context context = request.getContext(); if (context == null) { return; } if (!response.isError()) { // 當前請求沒錯誤 // 是一個原子類AtomicInteger errorState,如果大于0則認為遇到錯誤 return; } ErrorPage errorPage = context.findErrorPage(statusCode); // 這個地方以后再說,就是解決方案的一種,設置錯誤頁 if (errorPage == null) { // Look for a default error page errorPage = context.findErrorPage(0); } if (errorPage != null && response.isErrorReportRequired()) { ... image結合代碼和圖,再看白頁清楚的寫著This application has no explicit mapping for /error,路由為\error,這個緣由就是來自這里,然后進行forward跳轉,路由地址是\error
image后面使用了SpringBoot提供的白頁mv,渲染生產我們所看到的白頁頁面內容
至此,整個的流程也已經執行完成,總結一下就是請求一個不存在的鏈接,被發現是404請求之后轉發到/error的請求上
那么解決方案就很簡單了,有三種方案,不過這三種方案均是從不同的角度去解決該問題
- 添加攔截器
- 添加ErrorPage
- 添加/error 路徑
2、解決白頁問題
2.1、添加攔截器
public class CustomHandlerInterceptor implements HandlerInterceptor {攔截器在捕獲到/error 的請求之后,強制修改mv,使得最后渲染試用的mv是我們自定義設置的,而不是白頁內容,其中白頁本身的mv會經過ContentNegotiating視圖解析器處理成為ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration
注意這整個過期其實經過3次HTTP請求處理的,如下圖是使用HTTP事件監聽打印出的日志信息
?
經歷了/abc==>跳轉/err==>跳轉/error(并不顯示該內容,因為發送到瀏覽器的內容已經經過/err 渲染完成
其中真正的調用處理流程是/abc 沒有發現合適的handler,后選擇交由/error 路徑處理,只是后面又被攔截器攔截處理,轉發給了/err 處理
缺點:會攔截該路由的所有請求,包含靜態資源文件,對純提供接口的后端服務沒太多影響,其他的服務會有影響的
2.2、添加ErrorPage
添加合適的ErrorPage就不會出現跳轉到攔截器默認的/error 路徑,而是跳轉到自定義的ErrorPage上,這點在上面的status方法已經介紹其原因了
上述代碼添加了幾個錯誤頁ErrorPage跳轉的路徑以及其對應的HTTP錯誤碼,我們當前的樣例肯定是跳轉到/404連接上去了,再執行怎么又報錯了,原則上來說應該顯示404.html的內容文件,同時顯示的是經典的Tomcat錯誤頁了,如下圖頁面展示以及日志輸出的內容
imageimage
這個就是循環跳轉的問題
當系統沒有指定明確的視圖解析器之后,系統便會使用自帶的默認解析器InternalResourceView,在渲染前會去校驗當前url的問題,如果發現請求的url和目的url是一致的情況,就會認定轉發自身,出現Circular view path的問題
protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response) throws Exception { String path = getUrl(); if (this.preventDispatchLoop) { String uri = request.getRequestURI(); if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) { throw new ServletException("Circular view path [" + path + "]: would dispatch back " + "to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " + "(Hint: This may be the result of an unspecified view, due to default view name generation.)"); } } return path; }那么如何解決呢,肯定得從根本目的出發
- 添加模板解析器,這樣就不會使用默認解析器了
- 修改跳轉路徑
具體的解決方案就自行了解,本文不使用模板引擎渲染,直接展示body數據
image2.3、添加/error 路徑
上面已經知道了,既然默認的系統跳頁至/error,并且會完成數據渲染,那么我們自定義個/error的路由不就可以了,而且避免了靜態資源找不到的問題,不過注意這里存在一個問題,具體看下圖
image image先自行添加定義了一個很簡單的/error路徑的處理方法,但是在springboot啟動時,共有3個/error路徑的處理器方法,而且同時隸屬于同一個handle中,恰好經過URL路由規則處理,選擇了autoconfigure里面的handle,這肯定導致了我們自定義的/error 無效
image經過測試也確實無效,依舊顯示白頁,那么如何解決呢?同樣的方法有多種
- 根據路由匹配規則,修改適當內容,使得最后選擇處理器的時候達到我們自定義的處理器上,不過這點難度很大吧,需要對spring mvc 本身的路由映射規則了解的相當清楚,確保url映射處理的優先級問題等
-
上述我們已經知道了這三個/error是在RequestMappingHandlerMapping路由映射器中,我們可以使得自定義的處理器不存放在該路由映射其中,并且使得spring在輪詢時,優先處理約定的路由映射器即可,可是事實上handlerMapping中的BeanNameUrlMapping還在RequestMappingHandlerMapping之后,如果再去修改這個順序,同樣難度很大
image
EndpointHandlerMapping 是springboot的actuator模塊中的端點,自定義端點難度較大而且不適用在當前項目中
使用SimpleUrlHandlerMapping針對在springboot中不太合適,如果使用xml配置可直接設置其url會很方便的,如果強加在springboot中使用注解的方式需要額外配置,如下代碼
image雖然完成了/error 注入到SimpleUrlHandlerMapping中,可是即使添加了額外的配置依舊會出現無適配器的錯誤,這種方法并不適用
回過頭通過對BasicErrorController的觀察,我們可以自行繼承ErrorController接口完成
image到這里整個的過程就算是完成了
轉載于:https://www.cnblogs.com/davidwang456/articles/11011461.html
總結
以上是生活随笔為你收集整理的SpringBoot Whitelabel Error Page的根本原因,三种解决方案以及其特点的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring boot 在eclipse
- 下一篇: 从flink-example分析flin