javascript
apache 设置404 页面_SpringBoot自定义错误页面
SpringBoot請求錯誤如404可能看到如下頁面:
有時可能需要自定義錯誤頁面針對不同的http.status,如404/400。
【1】解決方法
① 注冊錯誤頁面
如下所示:
@Componentpublic?class?ErrorPageConfig?implements?ErrorPageRegistrar?{????@Override????public?void?registerErrorPages(ErrorPageRegistry?registry)?{????????ErrorPage?error400Page?=?new?ErrorPage(HttpStatus.BAD_REQUEST,?"/error/404");????????ErrorPage?error404Page?=?new?ErrorPage(HttpStatus.NOT_FOUND,?"/error/404");????????ErrorPage?error500Page?=?new?ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,?"/error/500");????????registry.addErrorPages(error400Page,error404Page,error500Page);????}}② controller進行攔截
然后你只需要寫個controller攔截不同請求然后跳到不同的自定義錯誤頁面即可,如下所示:
@RequestMapping("/error/{status}")public?String?errorPage(@PathVariable?Integer?status){????switch?(status){????????case?401:????????case?400:return?"/error/404";????????case?500:return?"/error/500";????????default:return?"/error/default";????}}那么原理呢?
【2】原理講解
① 啟動SpringBoot,注冊錯誤頁面
如下圖所示,啟動項目時候再onRefresh方法中會創建一個WebServer,繼而獲取ServletWebServerFactory。
1.1AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization
在創建bean-tomcatServletWebServerFactory時會調用AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization,如下所示:
@Overridepublic?Object?applyBeanPostProcessorsBeforeInitialization(Object?existingBean,?String?beanName)?throws?BeansException?{Object?result?=?existingBean;for?(BeanPostProcessor?processor?:?getBeanPostProcessors())?{?Object?current?=?processor.postProcessBeforeInitialization(result,?beanName);?if?(current?==?null)?{??return?result;?}?result?=?current;}return?result;}該方法會獲取bean后置處理器,然后循環遍歷調用每個bean后置處理器的postProcessBeforeInitialization方法。
1.2 ErrorPageRegistrarBeanPostProcessor.postProcessBeforeInitialization
當遍歷到ErrorPageRegistrarBeanPostProcessor時會調用其postProcessBeforeInitialization方法,方法源碼如下所示:
@Overridepublic?Object?postProcessBeforeInitialization(Object?bean,?String?beanName)?throws?BeansException?{?if?(bean?instanceof?ErrorPageRegistry)?{??postProcessBeforeInitialization((ErrorPageRegistry)?bean);?}?return?bean;}方法會判斷當前bean是否ErrorPageRegistry類型,如果是,則調用postProcessBeforeInitialization方法,源碼如下所示:
private?void?postProcessBeforeInitialization(ErrorPageRegistry?registry)?{?for?(ErrorPageRegistrar?registrar?:?getRegistrars())?{??registrar.registerErrorPages(registry);?}}該方法會獲取Registrars,然后循環遍歷調用每一個注冊器的registerErrorPages方法。獲取注冊其源碼如下所示:
private?Collection?getRegistrars()?{?if?(this.registrars?==?null)?{??//?Look?up?does?not?include?the?parent?context??this.registrars?=?new?ArrayList<>(????this.beanFactory.getBeansOfType(ErrorPageRegistrar.class,?false,?false).values());??this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);??this.registrars?=?Collections.unmodifiableList(this.registrars);?}?return?this.registrars;}故而,當我們的ErrorPageConfig 實現了ErrorPageRegistrar時,會被檢測到并執行registerErrorPages方法。
② 把錯誤頁面放到StandardContext.errorPageSupport中
StandardContext是什么?我們可以看下如下類繼承示意圖。
在①中我們提到會注冊錯誤頁面registrar.registerErrorPages(registry);,如下圖所示此時的registry為TomcatServletWebServerFactory:
我們再來看下TomcatServletWebServerFactory繼承示意圖(可以看到其父類AbstractConfigurableWebServerFactory實現了ErrorPageRegistry接口):
2.1 AbstractConfigurableWebServerFactory.addErrorPages
方法源碼如下:
@Overridepublic?void?addErrorPages(ErrorPage...?errorPages)?{?Assert.notNull(errorPages,?"ErrorPages?must?not?be?null");?this.errorPages.addAll(Arrays.asList(errorPages));}也就說錯誤頁面現在被放到了屬性private Set errorPages = new LinkedHashSet<>();中。
2.2 TomcatServletWebServerFactory.configureContext
創建完TomcatServletWebServerFactory后會調用configureContext方法,如下圖所示:
在configureContext方法中會獲取錯誤頁面然后逐個調用StandardContext.addErrorPage方法添加到其ErrorPageSupport errorPageSupport中。
configureContext方法中遍歷錯誤頁面如下所示:
for?(ErrorPage?errorPage?:?getErrorPages())?{?org.apache.tomcat.util.descriptor.web.ErrorPage?tomcatErrorPage?=?new?org.apache.tomcat.util.descriptor.web.ErrorPage();?tomcatErrorPage.setLocation(errorPage.getPath());?tomcatErrorPage.setErrorCode(errorPage.getStatusCode());?tomcatErrorPage.setExceptionType(errorPage.getExceptionName());?context.addErrorPage(tomcatErrorPage);}StandardContext.addErrorPage方法源碼如下所示:
@Override?public?void?addErrorPage(ErrorPage?errorPage)?{?????//?Validate?the?input?parameters?????if?(errorPage?==?null)?????????throw?new?IllegalArgumentException?????????????(sm.getString("standardContext.errorPage.required"));?????String?location?=?errorPage.getLocation();?????if?((location?!=?null)?&&?!location.startsWith("/"))?{?????????if?(isServlet22())?{?????????????if(log.isDebugEnabled())?????????????????log.debug(sm.getString("standardContext.errorPage.warning",??????????????????????????????location));?????????????errorPage.setLocation("/"?+?location);?????????}?else?{?????????????throw?new?IllegalArgumentException?????????????????(sm.getString("standardContext.errorPage.error",???????????????????????????????location));?????????}?????}//調用errorPageSupport.add?????errorPageSupport.add(errorPage);?????fireContainerEvent("addErrorPage",?errorPage);?}ErrorPageSupport.add方法如下所示:
public?void?add(ErrorPage?errorPage)?{?????String?exceptionType?=?errorPage.getExceptionType();?????if?(exceptionType?==?null)?{?????????statusPages.put(Integer.valueOf(errorPage.getErrorCode()),?errorPage);?????}?else?{?????????exceptionPages.put(exceptionType,?errorPage);?????}?}通過該方法可以看到,不止可以通過HTTP狀態碼定義錯誤頁面,還可以通過異常類型進行定義。
那么ErrorPageSupport、statusPages、exceptionPages分別是什么呢?我們看下圖示意:
③ 錯誤頁面如何被用到
在ResourceHttpRequestHandler.handleRequest方法處理請求時,找不到資源會調用response.sendError方法:
這里只需要關注這一點,無需關注細節,我們繼續往下走。。。。一直走到StandardHostValve.status方法。
StandardHostValve.status中會對響應狀態碼進行處理。
private?void?status(Request?request,?Response?response)?{?????int?statusCode?=?response.getStatus();?????//?Handle?a?custom?error?page?for?this?status?code?????Context?context?=?request.getContext();?????if?(context?==?null)?{?????????return;?????}?????/*?Only?look?for?error?pages?when?isError()?is?set.??????*?isError()?is?set?when?response.sendError()?is?invoked.?This??????*?allows?custom?error?pages?without?relying?on?default?from??????*?web.xml.??????*/?????if?(!response.isError())?{?????????return;?????}//這里會從errorPageSupport.find(errorCode)獲取到錯誤頁//根據錯誤碼,比如404從statusPages獲取對應的ErrorPage對象?????ErrorPage?errorPage?=?context.findErrorPage(statusCode);?????if?(errorPage?==?null)?{?????????//?Look?for?a?default?error?page?????????errorPage?=?context.findErrorPage(0);?????}?????if?(errorPage?!=?null?&&?response.isErrorReportRequired())?{?????????response.setAppCommitted(false);?????????request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,???????????????????????????Integer.valueOf(statusCode));?????????String?message?=?response.getMessage();?????????if?(message?==?null)?{?????????????message?=?"";?????????}?????????request.setAttribute(RequestDispatcher.ERROR_MESSAGE,?message);?????????request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,?????????????????errorPage.getLocation());?????????request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,?????????????????DispatcherType.ERROR);?????????Wrapper?wrapper?=?request.getWrapper();?????????if?(wrapper?!=?null)?{?????????????request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,???????????????????????????????wrapper.getName());?????????}?????????request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,??????????????????????????????request.getRequestURI());??????????????????????????????//這里很重要,將會嘗試跳轉到我們自定義錯誤請求頁面?????????if?(custom(request,?response,?errorPage))?{?????????????response.setErrorReported();?????????????try?{?????????????????response.finishResponse();?????????????}?catch?(ClientAbortException?e)?{?????????????????//?Ignore?????????????}?catch?(IOException?e)?{?????????????????container.getLogger().warn("Exception?Processing?"?+?errorPage,?e);?????????????}?????????}?????}?}如下圖所示,在StandardHostValve.custom方法中將會調用ApplicationDispatcher.forwar進行請求轉發。
版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:
https://blog.csdn.net/j080624/article/details/109197726
總結
以上是生活随笔為你收集整理的apache 设置404 页面_SpringBoot自定义错误页面的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: centos8安装
- 下一篇: springboot数据源不正确_Spr