springboot,使用自定义的格式向前端返回异常信息的json
最近进入了一家公司,这个公司使用的是完全前后端分离的架构,没有使用任何Java模板引擎,使用纯粹的js来渲染从后台传过来的数据。
这样的架构让人耳目一新,因为后台工作者完全不用涉及前端的东西。而且这样的框架有利于实现系统的高可用。
可是要搭建这样的框架,要解决的小问题有很多。例如,springboot默认返回的异常的格式是这样的:
<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'>Mon Apr 23 06:30:37 CST 2018</div>
<div>There was an unexpected error (type=Internal Server Error, status=500).</div>
<div>/ by zero</div>
</body>
</html>
仔细一看,发现springboot默认返回给前端的是一个html页面,这样前端就不能够定制异常显示页面了,只能把springboot默认返回的html页面显示给前端。这显然是不符合要求的。于是spring提供了一个@ControllerAdvice注解。
接下来我会向兄弟们详细介绍@ControllerAdvice注解。
该注解的基本格式是这样的:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //表明这个类会被spring的自动扫描机制所捕获,并被spring容器所维护。
public @interface ControllerAdvice {}
@ControllerAdvice有这几个关键点:
1.默认情况下,@ControllerAdvice类中的方法可以被所有的@Controller类所访问,但是可以使用选择器annotations(),basePackageClasses(),basePackages()来精细化目标@Controller类。
如果同时使用了多种选择器,它们之间的逻辑关系为“或”。注意:选择器验证是在运行时执行的,所以使用太多的选择器会影响性能。
2.可以定义多个@ControllerAdvice,它们被@Controller类访问的顺序默认由系统决定的。但是可以使用@Order注解改变。这个特性就叫@ControllerAdvice优先级。
3.对于异常处理,系统会选择第一个通知中的(优先级最高的那个@ControllerAdvice类)异常类型匹配的那个@ExceptionHandler方法。@ModelAttribute和@InitBinder方法是同样的道理,举一反三就可以了。
接下来就贴代码,看看实际应用中是如何应用@ControllerAdvice和@ExceptionHandler的。
public class ExceptionHandlerAdvice {
private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlerAdvice.class);
@ExceptionHandler(IllegalArgumentException.class)
@ResponseBody
public String handleIllegal(IllegalArgumentException e){
logger.error("非法参数",e);
//使用fastjson的响应对象格式
return ResponseJson.ResponseInfo(ApiResponse.INVALID_PARAM);
}
@ExceptionHandler(AuthenticationFailureException.class)
@ResponseBody
public String handleAuthenticationFailureException(AuthenticationFailureException e) {
return ResponseJson.ResponseInfo(ApiResponse.AUTHENTICATION_FAILURE);
}
@ExceptionHandler(DuplicateKeyException.class)
@ResponseBody
public String handlerDuplicateKeyException(DuplicateKeyException e){
logger.error("重复的唯一key", e);
return ResponseJson.ResponseInfo(ApiResponse.DUPLICATE);
}
@ExceptionHandler(BusinessException.class)
@ResponseBody
public String handleBusinessException(BusinessException e){
logger.error(e.message);
return ResponseJson.ResponseInfo(new ApiResponse(e));
}
@ExceptionHandler(MobileVerificationCodeErrorException.class)
@ResponseBody
public String handleMobileVerificationCodeErrorException(MobileVerificationCodeErrorException e) {
logger.error("验证码错误", e);
return ResponseJson.ResponseInfo(ApiResponse.VERIFICATION_CODE_ERROR);
}
@ExceptionHandler(LoginFailureException.class)
@ResponseBody
public String handleLoginFailureException(LoginFailureException e) {
logger.error("登录失败", e);
return ResponseJson.ResponseInfo(ApiResponse.LOGIN_FAILURE);
}
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseBody
public String handleNoHandlerFoundException(NoHandlerFoundException e) {
logger.error("地址请求异常:", e);
return ResponseJson.ResponseInfo(ApiResponse.EXCEPTION);
}
@ExceptionHandler(Exception.class)
@ResponseBody
public String handleException(Exception e) {
logger.error("Unhandled exception", e);
return ResponseJson.ResponseInfo(ApiResponse.EXCEPTION);
}
}
3.@ControllerAdvice类,并且该类是ResponseEntityExceptionHandler类的子类