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数据:
在这里插入图片描述以上是本人自学时候的一些感想,纯属小白,如果哪位大神觉得有问题可以进行评论。谢谢观看。

发布了6 篇原创文章 · 获赞 1 · 访问量 414

猜你喜欢

转载自blog.csdn.net/qq_42590394/article/details/105087247