日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot Whitelabel Error Page的根本原因,三种解决方案以及其特点

發布時間:2025/4/5 javascript 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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 { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 一定得為true,否則攔截器就無法生效了 // 當然可以隨意各種對url的攔截處理 return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { if (modelAndView != null) { // 防止出現空指針 // 在springboot中如果是錯誤頁肯定不會出現mv為null的情況 modelAndView.setViewName("/err"); // 注意:該請求只是測試試用,并沒有實際的意義 } } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } } @Bean public WebMvcConfigurerAdapter customMvcConfigurerAdapter (){ return new WebMvcConfigurerAdapter() { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CustomHandlerInterceptor()).addPathPatterns("/**"); // 添加攔截器 super.addInterceptors(registry); } }; }

攔截器在捕獲到/error 的請求之后,強制修改mv,使得最后渲染試用的mv是我們自定義設置的,而不是白頁內容,其中白頁本身的mv會經過ContentNegotiating視圖解析器處理成為ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration

注意這整個過期其實經過3次HTTP請求處理的,如下圖是使用HTTP事件監聽打印出的日志信息

image

?

經歷了/abc==>跳轉/err==>跳轉/error(并不顯示該內容,因為發送到瀏覽器的內容已經經過/err 渲染完成

其中真正的調用處理流程是/abc 沒有發現合適的handler,后選擇交由/error 路徑處理,只是后面又被攔截器攔截處理,轉發給了/err 處理

缺點:會攔截該路由的所有請求,包含靜態資源文件,對純提供接口的后端服務沒太多影響,其他的服務會有影響的

2.2、添加ErrorPage

添加合適的ErrorPage就不會出現跳轉到攔截器默認的/error 路徑,而是跳轉到自定義的ErrorPage上,這點在上面的status方法已經介紹其原因了

@Bean public EmbeddedServletContainerCustomizer containerCustomizer() { return new EmbeddedServletContainerCustomizer() { @Override public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) { ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST,"/400"); ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND,"/404"); ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,"/500"); configurableEmbeddedServletContainer.addErrorPages(errorPage400,errorPage404,errorPage500); } }; } // ======= 分割線 @ApiOperation("404請求") @GetMapping("404") public String e404() { return "404"; }

上述代碼添加了幾個錯誤頁ErrorPage跳轉的路徑以及其對應的HTTP錯誤碼,我們當前的樣例肯定是跳轉到/404連接上去了,再執行怎么又報錯了,原則上來說應該顯示404.html的內容文件,同時顯示的是經典的Tomcat錯誤頁了,如下圖頁面展示以及日志輸出的內容

image
image

這個就是循環跳轉的問題

當系統沒有指定明確的視圖解析器之后,系統便會使用自帶的默認解析器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數據

@RequestMapping("/") @RestController public class ErrorController { @ApiOperation("404請求") @GetMapping("404") public String e404() { System.out.println("404............"); return "這真的是一個404頁面,你看看"; } image

2.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中使用注解的方式需要額外配置,如下代碼

@RequestMapping("/") @Controller public class SimpleUrlController { private static final String ERROR_PATH = "/error"; @Resource private SimpleUrlHandlerMapping simpleUrlHandlerMapping; @PostConstruct public void init() { Map<String, Object> map = new HashMap<>(); map.put(ERROR_PATH, this); simpleUrlHandlerMapping.setUrlMap(map); simpleUrlHandlerMapping.initApplicationContext(); // 重新調用simpleurl映射 } @GetMapping("/error") @ResponseBody public String error(HttpServletRequest request) { System.out.println("SimpleUrlController"); Enumeration<String> attributes = request.getAttributeNames(); Map<String, Object> map = new HashMap<String, Object>(); while (attributes.hasMoreElements()) { String name = attributes.nextElement(); if (name.startsWith("java")) { // spring本身的屬性不宜對外暴露,切記! Object value = request.getAttribute(name); map.put(name, value); } } return JSON.toJSONString(map); } } image

雖然完成了/error 注入到SimpleUrlHandlerMapping中,可是即使添加了額外的配置依舊會出現無適配器的錯誤,這種方法并不適用

回過頭通過對BasicErrorController的觀察,我們可以自行繼承ErrorController接口完成

@RequestMapping("") @Controller public class CustomErrorController implements ErrorController { private static final String ERROR_PATH = "/error"; @GetMapping(ERROR_PATH) @ResponseBody public String error(HttpServletRequest request) { System.out.println("CustomErrorController"); Enumeration<String> attributes = request.getAttributeNames(); Map<String, Object> map = new HashMap<String, Object>(); while (attributes.hasMoreElements()) { String name = attributes.nextElement(); if (name.startsWith("java")) { // spring本身的屬性不宜對外暴露,切記! Object value = request.getAttribute(name); map.put(name, value); } } return JSON.toJSONString(map); } @Override public String getErrorPath() { return ERROR_PATH; } } image

到這里整個的過程就算是完成了

轉載于:https://www.cnblogs.com/davidwang456/articles/11011461.html

總結

以上是生活随笔為你收集整理的SpringBoot Whitelabel Error Page的根本原因,三种解决方案以及其特点的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。