当前源码:spring-boot 2.2.5.RELEASE
版本!
1.声明
当前内容用于本人复习和学习用,更加深入理解SpringBoot中的执行过程。本内容为:
1.通过实现方式创建自己的ErrorViewResolver,或者实现ErrorController方式
2.使用自定义的错误视图,告别SpringBoot默认提供的StaticView
3.更加深入了解Spring的访问机制
2.Spring的访问见解
1.首先默认访问一个正确的url,可以在spring中的requestmapping中获取到的,返回ModelAndView,然后通过ViewResolver解析ModelAndView获得View,最后调用View中的render方法返回
2.返回的视图为jsp,html,Spring就会读取该文件,返回
3.如果Spring返回的是json数据,则不会有ModelAndView返回null(因为数据返回在获取的时候就通过response返回了
)
==3.但是访问一个不存在的url的时候(默认返回为error),就会产生一个模板页面视图:StaticView
,具体查看:如何产生默认的错误视图 ==
例如:
个人感觉很别扭,但是本人决定将其变成错误的json信息并返回(所有的数据全部用json返回)
3.分析实现的流程
1.首先默认的error视图解析是通过DefaultErrorViewResolver进行解析的,所以可以通过实现ErrorViewResolver来自定义Error视图解析器
2.有了错误视图解析器,我们需要自定义错误视图,才能完成,这个可以参考StaticView,只要将其信息通过response返回即可
!
3.最后配置我的错误视图解析器即可
4.创建自己的ErrorViewResolver
@Component
public class JsonViewResolver implements ErrorViewResolver {
private final JsonView jsonView = new JsonView();
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
jsonView.setModel(model);
jsonView.setRequest(request);
jsonView.setStatus(status);
return new ModelAndView(jsonView);
}
}
这个JsonView,就是我们代替StaticView的替换类,这个类中保存了访问错误的url和其他的信息参数!
5.创建自己的错误视图JsonView
public class JsonView implements View{
private static final MediaType APPLICATION_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8);
private static final Log logger = LogFactory.getLog(View.class);
public JsonView() {
// TODO Auto-generated constructor stub
}
private HttpServletRequest request;
private HttpStatus status;
private Map<String, Object> model;
public HttpServletRequest getRequest() {
return request;
}
public HttpStatus getStatus() {
return status;
}
public Map<String, Object> getModel() {
return model;
}
public void setRequest(HttpServletRequest request) {
this.request = request;
}
public void setStatus(HttpStatus status) {
this.status = status;
}
public void setModel(Map<String, Object> model) {
this.model = model;
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws Exception {
if (response.isCommitted()) {
String message = getMessage(model);
logger.error(message);
return;
}
response.setContentType(APPLICATION_JSON_UTF8.toString());
StringBuilder builder = new StringBuilder();
Date timestamp = (Date) this.model.get("timestamp");
Object message = this.model.get("message");
Object trace = this.model.get("trace");
if (response.getContentType() == null) {
response.setContentType(getContentType());
}
builder.append("{");
builder.append("\"result\":\"error\",");
builder.append("\"path\":\""+this.model.get("path")+"\",");
builder.append("\"visitTime\":\""+timestamp+"\",");
builder.append("\"type\":\""+htmlEscape(this.model.get("error"))+"\",");
builder.append("\"status\":\""+htmlEscape(this.model.get("status"))+"\",");
builder.append("\"message\":\""+htmlEscape(message)+"\",");
builder.append("}");
/*
* builder.append("<html><body><h1>Whitelabel Error Page</h1>").append(
* "<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>"
* ) .append("<div id='created'>").append(timestamp).append("</div>")
* .append("<div>There was an unexpected error (type=").append(htmlEscape(model.
* get("error")))
* .append(", status=").append(htmlEscape(model.get("status"))).append(
* ").</div>"); if (message != null) {
* builder.append("<div>").append(htmlEscape(message)).append("</div>"); } if
* (trace != null) {
* builder.append("<div style='white-space:pre-wrap;'>").append(htmlEscape(trace
* )).append("</div>"); }
*/
/* builder.append("</body></html>"); */
response.getWriter().append(builder.toString());
}
private String htmlEscape(Object input) {
return (input != null) ? HtmlUtils.htmlEscape(input.toString()) : null;
}
private String getMessage(Map<String, ?> model) {
Object path = model.get("path");
String message = "Cannot render error page for request [" + path + "]";
if (model.get("message") != null) {
message += " and exception [" + model.get("message") + "]";
}
message += " as the response has already been committed.";
message += " As a result, the response may have the wrong status code.";
return message;
}
@Override
public String getContentType() {
return "application/json";
}
}
该JsonView中就是将当前的错误信息,使用json格式返回数据!
6.配置视图解析器
我们发现ErrorViewResolver本身并没有继承ViewResolver,所以不能在配置中添加,所以决定使用@Component注解修饰我们的JsonViewResolver
7.启动并访问
正常访问
错误访问
发现操作成功,成功替换掉当前SpringBoot的默认错误视图:StaticView
8.使用实现ErrorController方式配置(第二个方法,不建议使用)
通过SpringBoot文档发现另外一种解决方案:SpringBoot中的解决方案
使用该方案的实现:
@RestController
public class JsonErrorController implements ErrorController{
@RequestMapping(path = "/error")
public Map<String, Object> handle(HttpServletRequest request,Exception ex) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("status", request.getAttribute("javax.servlet.error.status_code"));
map.put("reason", request.getAttribute("javax.servlet.error.message"));
return map;
}
@Override
public String getErrorPath() {
// TODO Auto-generated method stub
return "error";
}
}
结果:
虽然这个也成功的实现了,但是通过debug发现使用这个方式实现的时候request,或者异常的信息丢失,没有前面的ErrorViewResolver和View方式好用,虽然这个方便但是无法获取其他的错误信息!
9.总结
1.当我们对Spring或者SpringBoot的某些功能感觉到不满意的时候,一定要对Spring有充分的理解
2.需要明白每个步骤实现什么功能,哪个功能在哪里实现的,这样我们才能正确修改,实现个人的需求
3.只有不断地作死,不断地了解Spring的源码才能走的更远!
以上纯属个人见解,如有问题请联系本人!