摘要:對應的請求信息如下如果是其他客戶端請求,如測試,會默認返回數據在之前的文章中介紹過了的自動配置機制,默認錯誤處理機制也是自動配置其中的一部分。在這個包中加載了所有的自動配置類,其中就是處理異常的機制。
??在我們開發的過程中經常會看到下圖這個界面,這是SpringBoot默認出現異常之后給用戶拋出的異常處理界面。
??對應的請求信息如下:
??如果是其他客戶端請求,如postman測試,會默認返回json數據
{ "timestamp":"2019-08-06 22:26:16", "status":404, "error":"Not Found", "message":"No message available", "path":"/asdad" }
??在之前的文章中介紹過了SpringBoot的自動配置機制,默認錯誤處理機制也是自動配置其中的一部分。在spring-boot-autoconfiguration-XXX.jar這個包中加載了所有的自動配置類,其中ErrorMvcAutoConfiguration就是SpringBoot處理異常的機制。
??下面簡單的分析一下它的機制
SpringBoot錯誤處理機制??首先看一下ErrorMvcAutoConfiguration這個類里面主要起作用的幾個方法
/** * 綁定一些錯誤信息 */ @Bean @ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT) public DefaultErrorAttributes errorAttributes() { return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException()); } /** * 默認錯誤處理 */ @Bean @ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT) public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) { return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers); } /** * 錯誤處理頁面 */ @Bean public ErrorPageCustomizer errorPageCustomizer() { return new ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath); } @Configuration static class DefaultErrorViewResolverConfiguration { private final ApplicationContext applicationContext; private final ResourceProperties resourceProperties; DefaultErrorViewResolverConfiguration(ApplicationContext applicationContext, ResourceProperties resourceProperties) { this.applicationContext = applicationContext; this.resourceProperties = resourceProperties; } /** * 決定去哪個錯誤頁面 */ @Bean @ConditionalOnBean(DispatcherServlet.class) @ConditionalOnMissingBean public DefaultErrorViewResolver conventionErrorViewResolver() { return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties); } }errorAttributes
??主要起作用的是下面這個類
org.springframework.boot.web.servlet.error.DefaultErrorAttributes
??這個類會共享很多錯誤信息,如:
errorAttributes.put("timestamp", new Date()); errorAttributes.put("status", status); errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase()); errorAttributes.put("errors", result.getAllErrors()); errorAttributes.put("exception", error.getClass().getName()); errorAttributes.put("message", error.getMessage()); errorAttributes.put("trace", stackTrace.toString()); errorAttributes.put("path", path);
??這些信息作為共享信息返回,所以當我們使用模板引擎時,也可以像取出其他參數一樣取出。
basicErrorController//org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController @Controller //定義請求路徑,如果沒有error.path路徑,則路徑為/error @RequestMapping("${server.error.path:${error.path:/error}}") public class BasicErrorController extends AbstractErrorController { //如果支持的格式 text/html @RequestMapping(produces = MediaType.TEXT_HTML_VALUE) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = getStatus(request); //獲取要返回的值 Mapmodel = Collections .unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); //解析錯誤視圖信息,也就是下面1.4中的邏輯 ModelAndView modelAndView = resolveErrorView(request, response, status, model); //返回視圖,如果沒有存在的頁面模板,則使用默認錯誤視圖模板 return (modelAndView != null) ? modelAndView : new ModelAndView("error", model); } @RequestMapping public ResponseEntity
??由上面的源碼可知,basicErrorControll主要用于創建請求返回的controller類,并根據http請求可接受的格式不同返回對應的信息。也就是我們在文章的一開始看到的情況,頁面請求和接口測試工具請求得到的結果略有差異。
errorPageCustomizer??errorPageCustomizer這個方法調了同類里面的ErrorPageCustomizer 這個內部類。當遇到錯誤時,如果沒有自定義error.path屬性,請求會轉發至/error
/** * {@link WebServerFactoryCustomizer} that configures the server"s error pages. */ private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered { private final ServerProperties properties; private final DispatcherServletPath dispatcherServletPath; protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) { this.properties = properties; this.dispatcherServletPath = dispatcherServletPath; } @Override public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { //getPath()得到如下地址,如果沒有自定義error.path屬性,則去/error位置 //@Value("${error.path:/error}") //private String path = "/error"; ErrorPage errorPage = new ErrorPage( this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath())); errorPageRegistry.addErrorPages(errorPage); } @Override public int getOrder() { return 0; } }conventionErrorViewResolver
??下面的代碼只展示部分核心方法
// org.springframework.boot.autoconfigure.web.servlet.error.DefaultErrorViewResolver public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered { static { Mapviews = new EnumMap<>(Series.class); views.put(Series.CLIENT_ERROR, "4xx"); views.put(Series.SERVER_ERROR, "5xx"); SERIES_VIEWS = Collections.unmodifiableMap(views); } @Override public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map model) { //使用HTTP完整狀態碼檢查是否有頁面可以匹配 ModelAndView modelAndView = resolve(String.valueOf(status.value()), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { //使用HTTP狀態碼第一位匹配初始化中的參數創建視圖對象 modelAndView = resolve(SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map model) { // 拼接錯誤視圖路徑 /error/{viewName} String errorViewName = "error/" + viewName; // 使用模板引擎嘗試創建視圖對象 TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext); if (provider != null) { return new ModelAndView(errorViewName, model); } //沒有模板引擎,使用靜態資源文件夾解析視圖 return resolveResource(errorViewName, model); } private ModelAndView resolveResource(String viewName, Map model) { // 遍歷靜態資源文件夾,檢查是否有存在視圖 for (String location : this.resourceProperties.getStaticLocations()) { try { Resource resource = this.applicationContext.getResource(location); resource = resource.createRelative(viewName + ".html"); if (resource.exists()) { return new ModelAndView(new HtmlResourceView(resource), model); } } catch (Exception ex) { } } return null; } }
??Thymeleaf對于錯誤頁面的解析如下:
public class ThymeleafTemplateAvailabilityProvider implements TemplateAvailabilityProvider { @Override public boolean isTemplateAvailable(String view, Environment environment, ClassLoader classLoader, ResourceLoader resourceLoader) { if (ClassUtils.isPresent("org.thymeleaf.spring5.SpringTemplateEngine", classLoader)) { String prefix = environment.getProperty("spring.thymeleaf.prefix", ThymeleafProperties.DEFAULT_PREFIX); String suffix = environment.getProperty("spring.thymeleaf.suffix", ThymeleafProperties.DEFAULT_SUFFIX); return resourceLoader.getResource(prefix + view + suffix).exists(); } return false; } }
??錯誤頁面首先會檢查模板引擎文件夾下的/error/HTTP狀態碼文件,如果不存在,則去檢查模板引擎下的/error/4xx或者/error/5xx文件,如果還不存在,則檢查靜態資源文件夾下對應的上述文件
自定義異常頁面??剛才分析了異常處理機制是如何工作的,下面我們來自己定義一個異常頁面。根據源碼分析可以看到,自定義錯誤頁面只需要在模板文件夾下的error文件夾下放置4xx或者5xx文件即可。
[[${status}]] 錯誤碼:[[${status}]]
信息:[[${message}]]
時間:[[${#dates.format(timestamp,"yyyy-MM-dd hh:mm:ss ")}]]
請求路徑:[[${path}]]
??隨意訪問不存在路徑得到下圖
自定義錯誤JSON??根據上面的錯誤處理機制可以得知,最終返回的JSON信息是從一個map對象轉換出來的,只需要自定義map中的值,就可以自定義錯誤信息的json了。直接重寫DefaultErrorAttributes類的 getErrorAttributes 方法即可。
/** * 自定義錯誤信息JSON值 */ @Component public class ErrorAttributesCustom extends DefaultErrorAttributes { //重寫getErrorAttributes方法 @Override public MapgetErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { //獲取原來的響應數據 Map map = super.getErrorAttributes(webRequest, includeStackTrace); String code = map.get("status").toString(); String message = map.get("error").toString(); HashMap hashMap = new HashMap<>(); //添加我們定制的響應數據 hashMap.put("code", code); hashMap.put("message", message); return hashMap; } }
??使用postman測試結果如下:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76103.html
摘要:前提好幾周沒更新博客了,對不斷支持我博客的童鞋們說聲抱歉了。熟悉我的人都知道我寫博客的時間比較早,而且堅持的時間也比較久,一直到現在也是一直保持著更新狀態。 showImg(https://segmentfault.com/img/remote/1460000014076586?w=1920&h=1080); 前提 好幾周沒更新博客了,對不斷支持我博客的童鞋們說聲:抱歉了!。自己這段時...
摘要:引入了新的環境和概要信息,是一種更揭秘與實戰六消息隊列篇掘金本文,講解如何集成,實現消息隊列。博客地址揭秘與實戰二數據緩存篇掘金本文,講解如何集成,實現緩存。 Spring Boot 揭秘與實戰(九) 應用監控篇 - HTTP 健康監控 - 掘金Health 信息是從 ApplicationContext 中所有的 HealthIndicator 的 Bean 中收集的, Spring...
閱讀 3296·2021-11-24 09:39
閱讀 3878·2021-11-22 09:34
閱讀 4824·2021-08-11 11:17
閱讀 1067·2019-08-29 13:58
閱讀 2580·2019-08-28 18:18
閱讀 547·2019-08-26 12:24
閱讀 834·2019-08-26 12:14
閱讀 739·2019-08-26 11:58