SpringBoot之自定义ErrorViewResolver和View或者ErrorController(告别SpringBoot的默认的错误视图,使用json返回错误信息)

当前源码: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的源码才能走的更远!

以上纯属个人见解,如有问题请联系本人!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值