默认效果:
1)、浏览器,返回一个默认的错误页面

  1.1 请求头

  1.2返回结果

2)、如果是其他客户端,默认响应一个json数据

2.1请求头

2.2返回结果

{
"timestamp": "2018-11-25T08:22:36.343+0000",
"status": ,
"error": "Not Found",
"message": "No message available",
"path": "/golden"
}

步骤:
  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 {
   // 系统出现错误以后来到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;
} } @Configuration
@ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", matchIfMissing = true)
@Conditional(ErrorTemplateMissingCondition.class)
protected static class WhitelabelErrorViewConfiguration {
//默认的SpringBoot错误页面
private final SpelView defaultErrorView = new SpelView(
"<html><body><h1>Whitelabel Error Page</h1>"
+ "<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>"
+ "<div id='created'>${timestamp}</div>"
+ "<div>There was an unexpected error (type=${error}, status=${status}).</div>"
+ "<div>${message}</div></body></html>"); @Bean(name = "error")
@ConditionalOnMissingBean(name = "error")
public View defaultErrorView() {
return this.defaultErrorView;
} // If the user adds @EnableWebMvc then the bean name view resolver from
// WebMvcAutoConfiguration disappears, so add it back in to avoid disappointment.
@Bean
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
} }
} 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);
//如果为空,返回error视图(在ErrorMvcConfiguration中配置的@Bean)
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; }

2)、如果定制错误响应

1、如何定制错误的页面
  1)、有模板引擎的情况下;error/状态码; 【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的error文件夹下】,发生此状态码的错误就会来到 对应的页面;
      我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html);
      页面能获取的信息:
      timestamp:时间戳
      status:状态码
      error:错误提示
      exception:异常对象
      message:异常消息
      errors:JSR303数据校验的错误都在这里
  2)、没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找
  3)、以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面

2、如何定制错误的json数据;
  1)、自定义异常处理&返回定制json数据;

一、页面请求出错


1. 自定义异常:

public class UserNotExistException extends RuntimeException {
private static final long serialVersionUID = -7200824453209817228L;
public UserNotExistException() {
super("用户不存在");
}
}

2. Controller调用

@Controller
public class HelloController {
@RequestMapping("/hello")
@ResponseBody
public String createInvoice(@RequestParam("user") String user) {
if(user.equals("aaa")) {
throw new UserNotExistException();
}
return "hello world";
}
}

3. 5xx.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>5xx.html</title>
</head>
<body>
timestamp:[[${timestamp}]] <br>
status:[[${status}]] <br>
error:[[${error}]] <br>
exception:[[${exception}]] <br>
errors:[[${errors}]] <br>
message:[[${message}]] <br>
</body>
</html>

4. 正确结果

5. 错误结果

 二、json错误定制


1.编写异常处理器

@ControllerAdvice
public class MyExceptionHandler {
//1、浏览器客户端返回的都是json
@ResponseBody
@ExceptionHandler(UserNotExistException.class)
public Map<String,Object> handleException(Exception e){
Map<String,Object> map = new HashMap<>();
map.put("code","user.notexist");
map.put("message",e.getMessage());
return map;
}
}

请求:

页面也显示json数据

但若想让浏览器返回错误页面,客户端返回json数据,且是自适应的,怎么办呢?

源码解析

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController { @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request); //status就是端口号
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);
} } public abstract class AbstractErrorController implements ErrorController {
protected HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request
.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
try {
return HttpStatus.valueOf(statusCode);
}
catch (Exception ex) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
}
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(UserNotExistException.class)
public String handleException(Exception e, HttpServletRequest request){
Map<String,Object> map = new HashMap<>();
//传入我们自己的错误状态码 4xx 5xx
/**
* Integer statusCode = (Integer) request
.getAttribute("javax.servlet.error.status_code");
*/
request.setAttribute("javax.servlet.error.status_code",);
map.put("code","user.notexist");
map.put("message","用户出错啦"); request.setAttribute("ext",map); //下面自定义异常返回结果时,将获取到此request作用域中的数据
//转发到/error
return "forward:/error";
}
}

但是这种方式无法返回map对象里自定义的数据到页面

Spring Boot 默认的实现原理如下:

public class BasicErrorController extends AbstractErrorController {
   //SpringBoot 默认的返回错误html页面代码
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
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);
}

  //Spring Boot 默认的返回json数据请求
@RequestMapping
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 abstract class AbstractErrorController implements ErrorController {

    private final ErrorAttributes errorAttributes;

    protected Map<String, Object> getErrorAttributes(HttpServletRequest request,
boolean includeStackTrace) {
WebRequest webRequest = new ServletWebRequest(request);
return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
}
} @Order(Ordered.HIGHEST_PRECEDENCE)
public class DefaultErrorAttributes
implements ErrorAttributes, HandlerExceptionResolver, Ordered { @Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<>();
errorAttributes.put("timestamp", new Date());
addStatus(errorAttributes, webRequest);
addErrorDetails(errorAttributes, webRequest, includeStackTrace);
addPath(errorAttributes, webRequest);
return errorAttributes;
}
}

3)、将我们的定制数据携带出去
出现错误以后,会来到/error请求,会被BasicErrorController处理,响应出去可以获取的数据是由
getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法);
  1、完全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中;
  2、页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到;
容器中DefaultErrorAttributes.getErrorAttributes();默认进行数据处理的;

@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class,WebMvcProperties.class })
public class ErrorMvcAutoConfiguration { private final ServerProperties serverProperties; //当容器中没有ErrorAttributes类型的bean时,才走默认的,所以我们自定义ErrorAttributes的实现类就可以实现定制化
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes(
this.serverProperties.getError().isIncludeException());
}
}

Spring Boot1.5.10版本

//给容器中加入我们自己定义的ErrorAttributes,用其实现类,避免重写多个无需改造的方法
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
  @Override
  public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,boolean includeStackTrace) {
    Map<String, Object> map = super.getErrorAttributes(requestAttributes,includeStackTrace);
    map.put("company","everjiankang"); //自定义属性
    map.put("name","超轶绝尘");
    return map;
  }
}

Spring Boot 2.0 版本

import java.util.Map;

//
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;  servlet的可以成功执行
//import org.springframework.boot.web.reactive.error.DefaultErrorAttributes; reactive 也有这个类,但是执行不了
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest; @Component
public class MyErrorAttributes extends DefaultErrorAttributes{
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
    //获取到上文中异常处理类中设置到request作用域中的“ext”属性 requeste.setAtrribute("exgt",map);
     Map<String,Object> extMap = (Map<String, Object) webRequest.getAttribute("ext", RequestAttributes.SCOPE_REQUEST);
        Map<String,Object> map =  super.getErrorAttributes(webRequest, includeStackTrace);
map.put("name", "xiaochao");
     map.put("ext", extMap); //将异常处理类中的自定义信息返回给页面
      
return map;
} } public interface WebRequest extends RequestAttributes { }

 public interface RequestAttributes {

    int SCOPE_REQUEST = 0;

    int SCOPE_SESSION = 1;

 }


Spring Webflux 版本,执行未成功

在Spring boot 2.1.0版本中,第一个参数变成了org.springframework.web.reactive.function.server.ServerRequest类型,需要引入jar包

但是此段代码不能适用。如何自定义详情查看Spring Boot官方文档

Spring 5 之 Spring Webflux 开发 Reactive 应用

<!-- 此依赖 会依赖于Netty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId> <!--【改】增加“flux”四个字符-->
</dependency>
//import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;   reactive 也有这个类,但是执行不了



16. Spring boot 错误页面的更多相关文章

  1. Spring boot错误处理以及定制错误页面

    如果是浏览器访问,返回错误页面 注意浏览器发送请求的请求头:  注意区别其他客户端哦比如 postman 如果是其他客户端,返回一个Json数据 原理可以参照ErrorMvcAutoConfigura ...

  2. spring boot 错误处理之深度历险

    今天终于把 boot 的异常处理完全研究透了: boot提供了很多错误的处理工作.默认情况下,我们会看到一个whiteLabel(白标)的页面. 这个可能不是我们所需.因此我们需要定制.我于是做了个深 ...

  3. Spring MVC错误页面配置

    当前SpringMVC非常流行,在大多数情况,我们都需要自定义一些错误页面(例如:401, 402, 403, 500...),以便更友好的提示.对于spring mvc,这些当然是支持自定义的,sp ...

  4. spring boot错误: 找不到或无法加载主类

    一:当在eclipse启动spring boot项目时出现问题: springboot错误: 找不到或无法加载主类 解决办法: 1,通过cmd命令行,进入项目目录进行,mvn clean instal ...

  5. Spring Boot 静态页面

    spring boot项目只有src目录,没有webapp目录,会将静态访问(html/图片等)映射到其自动配置的静态目录,如下 /static /public /resources /META-IN ...

  6. Spring Boot 静态页面跳转

    本篇博客仅为自己提个醒:如何跳转页面而不麻烦控制器. 当我们创建 Spring Boot 项目时(勾选了 Thymeleaf 和 Web),目录结构会是如下:        其中图二是我创建了一个 h ...

  7. Spring Boot错误——SpringBoot 2.0 报错: Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

    背景 使用Spring Cloud搭建微服务,服务的注册与发现(Eureka)项目启动时报错,错误如下 *************************** APPLICATION FAILED T ...

  8. Spring boot 错误处理机制

    请求方式时,若不存在 浏览器出现White label Error Page 错误页面 其他客户端出现响应一个JSON格式文本包含错误码等信息 浏览器发送请求的请求头: 客户端请求头 这样就能区分来自 ...

  9. spring boot 错误处理总结

    在boot 中, 对404  和 异常 有了额外的处理. 当然,我们可以定制, 如何做呢? 1 写一个继承 ErrorController 的Controller 注意, 这里一定要继承 ErrorC ...

随机推荐

  1. cerr与cout的区别

    1.cout经过缓冲后输出,默认情况下是显示器.可以重定向. 2.cerr不经过缓冲而直接输出,一般用于迅速输出出错信息,不可以被重定向.

  2. ABP框架学习

    一.总体与公共结构 1,ABP配置 2,多租户 3,ABP Session 4,缓存 5,日志 6,设置管理 7,Timing 8,ABPMapper 9,发送电子邮件 二.领域层 10,实体 11, ...

  3. 认真学习Linux系统让你真的有收获

    学习,最重要的是有一个认真的态度,熟话说,读书可以改变命运,以前觉得并不一定是这样,其实经历的多了才知道,事实确实如此.试想一个不会写字看书的人,他的生活该有多么无聊.读书容易,但读好书并不总是件容易 ...

  4. hdu 6394 Tree (2018 Multi-University Training Contest 7 1009) (树分块+倍增)

    链接: http://acm.hdu.edu.cn/showproblem.php?pid=6394 思路:用dfs序处理下树,在用分块,我们只需要维护当前这个点要跳出这个块需要的步数和他跳出这个块去 ...

  5. day29 __eq__ 比较

    本质上 "==" 调用的内部方法就是 __eq__() 正常情况下,两个名字相同的变量比较的是内存地址,内存地址当然是不一样的可以使用__eq__来改变成名字相同就相等 1 cla ...

  6. day24 包

    # 把解决一类问题的模块会被放在一个文件夹里面,即包 # import os # os.makedirs('glance/api') # os.makedirs('glance/cmd') # os. ...

  7. javascript history.go(-1) 返回刷新不起作用

    js返回刷新两种方式: 方式一:有提示框 <input type="button"  onClick="javascript:history.go(-1);loca ...

  8. 11 Zabbix Item类型之Zabbix Calculated 计算型Item类型

    点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 11 Zabbix Item类型之Zabbix Calculated 计算型Item类型 计算类型 ...

  9. oracle导出导入数据库

    一.给空表分配空间: 这一步一定要做,否则空表不能导出. 首先连接你要导出的库,在该库上执行以下sql: select 'alter table '||table_name||' allocate e ...

  10. 51nod 1462 树据结构 | 树链剖分 矩阵乘法

    题目链接 51nod 1462 题目描述 给一颗以1为根的树. 每个点有两个权值:vi, ti,一开始全部是零. Q次操作: 读入o, u, d o = 1 对u到根上所有点的vi += d o = ...