1、只要在templates文件夹下面加上error文件夹,然后将html文件名字命名为状态码就可以出现自己定制的错误页面了
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
出现了404错误<br>
timestamp:<span th:text="${timestamp}"></span><br>
status:<span th:text="${status}"></span><br>
error:<span th:text="${error}"></span><br>
message:<span th:text="${message}"></span><br>
</body>
</html>
去访问一个项目中不存在的地址
2、如果是5xx的错误类似下面这个例子
1、创建一个Exception用来待会抛出异常
package org.xjj.demo98001.exception;
public class MyException extends RuntimeException{
public MyException() {
super("出现了异常");
}
}
2、controller,当传值为123的时候手动抛出异常
package org.xjj.demo98001.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.xjj.demo98001.exception.MyException;
@Controller
public class MyController {
@RequestMapping("/toException")
public String toException(@RequestParam int id){
if(id==123)
throw new MyException();
return "success";
}
}
3、在5xx的页面上输出相关信息(但是在SpringBoot2中要对application.properties进行配置,否则不会出现exception)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
出现了500错误<br>
timestamp:<span th:text="${timestamp}"></span><br>
status:<span th:text="${status}"></span><br>
exception:<span th:text="${exception}"></span><br>
message:<span th:text="${message}"></span><br>
</body>
</html>
4、对application.properties的配置
#用来配置错误页面的时候可以拿到exception
server.error.include-exception=true
去发送controller中定制好的请求 http://localhost:8080/toException?id=123
3、原理:对于错误页面的配置都在ErrorMvcAutoConfiguration(以下四个bean是对错误页面的主要配置)
1、ErrorPageCustomizer
@Bean
public ErrorMvcAutoConfiguration.ErrorPageCustomizer errorPageCustomizer() {
return new ErrorMvcAutoConfiguration.ErrorPageCustomizer(this.serverProperties, this.dispatcherServletPath);//1、要返回ErrorPageCustomizer这个内部类
}
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {
private final ServerProperties properties;
private final DispatcherServletPath dispatcherServletPath;
protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {//2、需要获得跳转的路径
this.properties = properties;
this.dispatcherServletPath = dispatcherServletPath;
}
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));//3、getPath用来获得跳转路径
errorPageRegistry.addErrorPages(new ErrorPage[]{errorPage});
}
public int getOrder() {
return 0;
}
}
public class ErrorProperties {
@Value("${error.path:/error}")
private String path = "/error";//4、获取跳转路径是在error文件夹下
}
2、BasicErrorController
@Bean
@ConditionalOnMissingBean(
value = {ErrorController.class},
search = SearchStrategy.CURRENT
)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);//5、要返回这个new的类
}
@Controller//这是一个controller就是相当于每个error请求也就是通过controller跳转的
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
@RequestMapping(produces = {"text/html"})
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);//6、依靠这个方法去返回一个错误视图界面,如果视图不为空就直接返回,为空就返回默认的
return modelAndView != null ? modelAndView : new ModelAndView("error", model);
}
}
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
Iterator var5 = this.errorViewResolvers.iterator();
ModelAndView modelAndView;
do {
if (!var5.hasNext()) {
return null;
}
ErrorViewResolver resolver = (ErrorViewResolver)var5.next();
modelAndView = resolver.resolveErrorView(request, status, model);//7、遍历容器中所有的错误视图解析器,如果没有符合条件的就为空
} while(modelAndView == null);
return modelAndView;
}
3、DefaultErrorViewResovler
@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() {//9、由它来解析得到跳转的路径
return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
}
}
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
private ModelAndView resolve(String viewName, Map<String, Object> model) {
String errorViewName = "error/" + viewName;//8、跳转路径为templates下的error文件夹
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);
}
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
String[] var3 = this.resourceProperties.getStaticLocations();
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
String location = var3[var5];
try {
Resource resource = this.applicationContext.getResource(location);
resource = resource.createRelative(viewName + ".html");//拼接视图解析器
if (resource.exists()) {
return new ModelAndView(new DefaultErrorViewResolver.HtmlResourceView(resource), model);
}
} catch (Exception var8) {
}
}
return null;
}
4、DefaultErrorAttributes
@Bean
@ConditionalOnMissingBean(
value = {ErrorAttributes.class},
search = SearchStrategy.CURRENT
)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());//用来获取错误页面的信息
}
public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {
private static final String ERROR_ATTRIBUTE = DefaultErrorAttributes.class.getName() + ".ERROR";
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap();
errorAttributes.put("timestamp", new Date());
this.addStatus(errorAttributes, webRequest);
this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);
this.addPath(errorAttributes, webRequest);
return errorAttributes;
}
private void addStatus(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {
Integer status = (Integer)this.getAttribute(requestAttributes, "javax.servlet.error.status_code");
if (status == null) {
errorAttributes.put("status", 999);
errorAttributes.put("error", "None");
} else {
errorAttributes.put("status", status);
try {
errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
} catch (Exception var5) {
errorAttributes.put("error", "Http Status " + status);
}
}
}
private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest, boolean includeStackTrace) {
Throwable error = this.getError(webRequest);
if (error != null) {
while(true) {
if (!(error instanceof ServletException) || error.getCause() == null) {
if (this.includeException) {
errorAttributes.put("exception", error.getClass().getName());
}
this.addErrorMessage(errorAttributes, error);
if (includeStackTrace) {
this.addStackTrace(errorAttributes, error);
}
break;
}
error = error.getCause();
}
}
Object message = this.getAttribute(webRequest, "javax.servlet.error.message");
if ((!StringUtils.isEmpty(message) || errorAttributes.get("message") == null) && !(error instanceof BindingResult)) {
errorAttributes.put("message", StringUtils.isEmpty(message) ? "No message available" : message);
}
}
}
, “javax.servlet.error.message”);
if ((!StringUtils.isEmpty(message) || errorAttributes.get(“message”) == null) && !(error instanceof BindingResult)) {
errorAttributes.put(“message”, StringUtils.isEmpty(message) ? “No message available” : message);
}
}
}
####