1. 程式人生 > >Spring boot 錯誤頁面

Spring boot 錯誤頁面

 

預設效果:
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;

}