SpringBoot错误信息
SpringBoot中错误信息的详解以及自定义错误信息
最近一直在学习SpringBoot,对于SpringBoot的学习感到还是很有难度的,因为要不断的去查阅源码才能深入的了解,本篇文章主要是想记录下这几天一直研究的SpringBoot中错误信息这一个部分。
SpringBoot错误初体验
在官方文档中是这样写的,在这我给进行了翻译,有兴趣的可以看一下:
那么在真实的项目中,如果出错了,SpringBoot会展现出什么样呢?
1.在浏览器中会出现这样的提示信息等
2.如果是别的客户端进行访问出现错误会显示这样的JSON数据。
SpringBoot错误原理分析
在SpringBoot中ErrorMvcAutoConfiguration这个配置类来处理错误的信息
在这个类中注册了以下几个类:
1.DefaultErrorAttributes
2.BasicErrorController
3.ErrorPageCustomizer
4.DefaultErrorViewResolver
我们分别说一下这几个类是干啥的,首先,我们来看,当SpringBoot出现错误的时候会来到ErrorPageCustomizer这个类中,
该类中有这个方法:
@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
ErrorPage errorPage = new ErrorPage(
this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
errorPageRegistry.addErrorPages(errorPage);
}
该方法最终是发送/error请求
@Value("${error.path:/error}")
private String path = "/error";
在发送该请求后BasicErrorController进行处理(在该类中队/error请求进行了两个不同的处理):
其中一个进行浏览器的页面处理,另一个负责客户端的页面处理,具体的代码如下
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")//处理/error请求
public class BasicErrorController extends AbstractErrorController {
//将会产生html的数据;给浏览器发送的请求
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
//解析错误的信息DefaultErrorAttributes类中
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
//去哪个页面是错误的页面:包含页面的地址以及页面的内容,AbstractErrorController类中的方法
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
//产生json数据,其他的客户端(不需要跳转页面)
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
//获取状态码
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
//解析错误的信息DefaultErrorAttributes类中
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
}
//AbstractErrorController类中resolveErrorView方法是ErrorViewResolver接口中的resolveErrorView,
//DefaultErrorViewResolver实现了ErrorViewResolver接口
AbstractErrorController类中:
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status,
Map<String, Object> model) {
//所有的ErrorViewResolver的到modelAndView
for (ErrorViewResolver resolver : this.errorViewResolvers) {
ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
if (modelAndView != null) {
return modelAndView;
}
}
return null;
}
接下来就是错误页面消息以及相应的页面了,从上面的代码可以看见,要去哪个页面是由DefaultErrorViewResolver类决定的,具体代码如下:
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
//视图的名字就是status.value()出现错误的状态码(404,500.。)
ModelAndView modelAndView = resolve(String.valueOf(status.value()), 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);
//模板引擎可用的情况下返回errorViewName指定的视图地址
if (provider != null) {
return new ModelAndView(errorViewName, model);
}
//模板引擎不可用,就在静态资源文件夹下面找errorViewName对应的页面 相当于404.html
return resolveResource(errorViewName, model);
}
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
//遍历静态资源文件
for (String location : this.resourceProperties.getStaticLocations()) {
try {
Resource resource = this.applicationContext.getResource(location);
//在静态文件下去查找状态码.html文件 例如 404.html
resource = resource.createRelative(viewName + ".html");
if (resource.exists()) {
return new ModelAndView(new HtmlResourceView(resource), model);
}
}
catch (Exception ex) {
}
}
return null;
}
对于错误信息来说,在BasicErrorController类中通过调用DefaultErrorAttributes 中的getErrorAttributes方法进行错误消息的处理:
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<>();
//时间戳
errorAttributes.put("timestamp", new Date());
//状态码
addStatus(errorAttributes, webRequest);
//错误提示 exception:异常提示 message:异常消息 errors:JSR303数据校验的错误
addErrorDetails(errorAttributes, webRequest, includeStackTrace);
addPath(errorAttributes, webRequest);
return errorAttributes;
}
...
SpringBoot定制错误页面
1.在有模板引擎的情况下:error/状态码;【将错误页面命名为错误状态码,错误状态码.html,放在木板引擎文件里面的error文件下】,发生此状态码的错误就来到这个页面
我们可以使用4xx和5xx作为错误页面的文件名匹配这种类型的所有错误,精确优先(优先寻找精确状态码)
2.如果没有模板引擎(在模板引擎下找不到),静态资源文件下找。
3.都没有;就是默认来到springBoot默认的错误提示页面
在定制的错误页面中可以采用以下的形式进行定制:
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<h1>status:[[${status}]]</h1>
<h2>timestamp:[[${timestamp}]]</h2>
</main>
页面会输出响应的内容:
SpringBoot定制错误的信息
继承DefaultErrorAttributes 类,重写getErrorAttributes方法,利用父类中的getErrorAttributes,获取基本的错误信息,并且添加自己的错误信息
@Component
class MyErrorAttributes extends DefaultErrorAttributes {
//返回值的map就是页面和json能获取的所有字段
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest,includeStackTrace);
//添加自己的错误信息
errorAttributes.put("company", "cyk");
return errorAttributes;
}
public MyErrorAttributes(){
super(true);
}
}
在客服端中可以清楚的看到:
自定义错误以及相应的输出
当然,还有更加高级的直接定制,别我们直接自定义一个错误:
public class UserNotExistException extends RuntimeException{
public UserNotExistException(){
super("用户不存在");
}
}
写一个类专门进行处理该错误,并写入自己的错误信息,并将信息放入request中。最终发出/error请求。
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(UserNotExistException.class)
public String handleException(Exception e, HttpServletRequest request){
Map<String,Object> map = new HashMap<>();
//传入我们自己的错误状态码
request.setAttribute("javax.servlet.error.status_code",400);
map.put("code","user.notexist");
map.put("message",e.getMessage());
// 转发到/error请求
request.setAttribute("ext",map);
return "forward:/error";
}
}
在Contorller中定义一个请求,进行错误的调用:
@RequestMapping("/hello")
@ResponseBody
public String hello(@RequestParam("user")String user){
if (user.equals("aaa")){
//调用该错误
throw new UserNotExistException();
}
return "success";
}
在通过继承DefaultErrorAttributes 类,重写getErrorAttributes方法,利用父类中的getErrorAttributes,获取基本的错误信息,并且添加自己的错误信息,将request中的错误信息也进行添加。
@Component
class MyErrorAttributes extends DefaultErrorAttributes {
//返回值的map就是页面和json能获取的所有字段
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest,includeStackTrace);
errorAttributes.put("company", "cyk");
Map<String, Object> ext = (Map<String, Object>) webRequest.getAttribute("ext", 0);
errorAttributes.put("ext",ext);
return errorAttributes;
}
public MyErrorAttributes(){
super(true);
}
}
最终可以输入以下的json数据:
以上是本人自学时候的一些感想,纯属小白,如果哪位大神觉得有问题可以进行评论。谢谢观看。