Spring boot 錯誤頁面
阿新 • • 發佈:2018-11-24
預設效果:
1)、瀏覽器,返回一個預設的錯誤頁面
1.1 請求頭
1.2返回結果
2)、如果是其他客戶端,預設響應一個json資料
2.1請求頭
2.2返回結果
步驟:
1)系統出現4xx或者5xx之類的錯誤;ErrorPageCustomizer就會生效(定製錯誤的響應規則);
2) 根據相應規則來到/error請求;被BasicErrorController處理;
3)響應頁面;被Controller處理後去哪個頁面是由DefaultErrorViewResolver解析得到的;
原始碼解析
public class ErrorMvcAutoConfiguration {
// 系統出現錯誤以後來到error請求進行處理;(相當於web.xml註冊錯誤頁面規則) @Bean public ErrorPageCustomizer errorPageCustomizer() { return new ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath); } /** * {@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) { ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath .getRelativePath(this.properties.getError().getPath())); errorPageRegistry.addErrorPages(errorPage); } @Override public int getOrder() { return 0; } } } public class ErrorProperties { /** * Path of the error controller. */ @Value("${error.path:/error}") private String path = "/error"; }
public class ErrorMvcAutoConfiguration { @Bean @ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT) public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) { return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers); } } public abstract class AbstractErrorController implements ErrorController { private final ErrorAttributes errorAttributes; private final List<ErrorViewResolver> errorViewResolvers; public AbstractErrorController(ErrorAttributes errorAttributes) { this(errorAttributes, null); } //解析錯誤頁面 protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) { for (ErrorViewResolver resolver : this.errorViewResolvers) { ModelAndView modelAndView = resolver.resolveErrorView(request, status, model); if (modelAndView != null) { return modelAndView; } } return null; } } /**取出配置項:server.error.path中的值。如果沒有,則取error.path的值,如果還沒有,則預設為/error路徑*/ @Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class BasicErrorController extends AbstractErrorController { @RequestMapping(produces = "text/html")//產生html型別的資料;瀏覽器傳送的請求來到這個方法處理 public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes( request, isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); //去哪個頁面作為錯誤頁面;包含頁面地址和頁面內容 ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView != null) ? modelAndView : new ModelAndView("error", model); } //產生json資料,其他客戶端來到這個方法處理; @RequestMapping @ResponseBody public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); HttpStatus status = getStatus(request); return new ResponseEntity<>(body, status); } } public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered { private static final Map<Series, String> SERIES_VIEWS;
private final ResourceProperties resourceProperties; static { Map<Series, String> views = 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<String, Object> model) { ModelAndView modelAndView = resolve(String.valueOf(status), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { modelAndView = resolve(SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) {
//預設SpringBoot可以去找到一個頁面? error/404 String errorViewName = "error/" + viewName;
//模板引擎可以解析這個頁面地址就用模板引擎解析 TemplateAvailabilityProvider provider = this.templateAvailabilityProviders .getProvider(errorViewName, this.applicationContext); if (provider != null) {
//模板引擎可用的情況下返回到errorViewName指定的檢視地址 return new ModelAndView(errorViewName, model); }
//模板引擎不可用 return resolveResource(errorViewName, model); } // private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
//從靜態資原始檔夾下解析對應的頁面 error/404.html 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; } }
靜態資原始檔夾路徑
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false) public class ResourceProperties { private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" }; /** * Locations of static resources. Defaults to classpath:[/META-INF/resources/, * /resources/, /static/, /public/]. */ private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS; }