spring boot 默认异常处理的实现
本周在看陈杰写的自定义异常的微信异常时,使用的是自定义异常状态码和信息,在出错时将他抛出,并用@exceptionhandler注解定义一个全局异常处理器,根据异常的内容向前台发送状态码和信息,处理异常的代码如下图:
//处理微信登录的异常 @exceptionhandler(value = wechatloginexception.class) public string wechatloginexceptionhandler(httpservletrequest request, httpservletresponse response, wechatloginexception e){ logger.error("微信登录异常:---host {} invokes url {} error: {}", request.getremotehost(), request.getrequesturl(), e.getmessage()); response.setstatus(e.getcode()); return e.getmessage(); }
在这里我看的时候有点疑惑,将状态码写入响应,而信息却直接返回了,询问陈杰,前台果然没有接受到e.getmessage()的信息,我上网搜索了一下,推荐他使用response.senderror(code, message)这个方法来返回异常的信息,但是这么一试之后却遭到了奇怪的问题.
莫名的拦截器
项目配置了一个拦截器,专门用来对用户进行验证是否登录的,这个是前提.在使用response.setstatus()方法时,前台能正确的接受到传入的状态码,而使用response.senderror()时,前台却接受到的一直是401用户未登录的状态码,打了断点进行调试,分别在拦截器,跑出异常的方法,处理异常的方法打上断点,测试使用response.setstatus()和response.senderror()方法来查看执行顺序,结果让我感到惊奇:
使用response.setstatus()执行顺序:
使用response.senderror()执行顺序:
出现了令人惊奇两点:
1.setstatus()请求时没有经过拦截器
2.senderror()在异常处理完毕后经过了一次拦截器
查看注册拦截器配置,解决了第一个问题的疑惑:
public void addinterceptors(interceptorregistry registry) { // 添加拦截器,去除对登录的拦截 registry.addinterceptor(authinterceptor) .excludepathpatterns("/user/login") .excludepathpatterns("/user/wechatlogin"); }
这个异常是用户登录时抛出的,在注册时将登录路径给忽略了,因为我们只是拦截未登录的请求,而请求登录的请求不应该拦截,这是正确的,但第二点却怎么也不明白,本应忽略拦截的请求,为什么换了senderror()方法后,却在异常处理完毕后经过了异常拦截器?
springboot的默认异常处理
对比两个方法的不同:setstatus()只是改了一下状态吗,而senderror()还有请求错误的意味,于是猜想是不是请求错误才会出现这种情况,将方法直接改为throw new runtimeexception()(没有处理异常),发现拦截器拦截的请求的url居然是一个/error的url.
这个/error的url并未在项目中定义过任何的控制器中,也从未发起这样的请求,上网一查询,原来这是spring boot提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局的错误页面用来展示异常内容.
但是我们的拦截器把这个请求拦截了(并且这个请求没有携带正确的cookie),所以直接就返回了401错误,response中也没有我们定义的状态码和信息了.
json还是html
一切真相大白了,但忽然想到如果是浏览器发起的请求,服务器错误后springboot默认异常处理返回的是html,但是如果像我们前后台分离的请求,返回就不应该是html而是json的错误信息了,这个要怎么区分呢?
使用google插件发送请求,返回的body是这样的:
而用浏览器发起的请求返回的却是一个html页面:
<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'>sat apr 13 21:34:34 cst 2019</div> <div>there was an unexpected error (type=internal server error, status=500).</div> <div>no message available</div> </body> </html>
仔细查看两者发起的请求不同,在浏览器发起请求信息requestheader上发现了accept字段:
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
顿时就明白了,在发送请求时spring-boot根据accept字段来给你返回响应的内容,例如application/json返回json,text/html返回html,真是感叹spring-boot真是太周全了.
总结
spring-boot好心帮你默认请求异常,但是却给你带来了麻烦,感觉还是自己理解的不够多,学习路还远着呢。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Spring Boot全局异常处理解析
-
spring boot 默认异常处理的实现
-
如何优雅的处理Spring Boot异常信息详解
-
Spring Boot与Kotlin处理Web表单提交的方法
-
详解Spring Boot 中实现定时任务的两种方式
-
Spring boot + thymeleaf 后端直接给onclick函数赋值的实现代码
-
spring-boot-starter-web更换默认Tomcat容器的方法
-
Spring Boot实现通用的接口参数校验
-
spring boot整合Shiro实现单点登录的示例代码
-
Spring Boot+Mybatis+Druid+PageHelper实现多数据源并分页的方法