spring 或 springboot统一异常处理

一,本文介绍spring MVC的自定义异常处理,即在Controller中抛出自定义的异常时,客户端收到更友好的JSON格式的提示。而不是常见的报错页面。

二,场景描述:实现公用API,验证API key的逻辑,放在拦截器中判断(等同于在Controller中)并抛出异常,用户收到类似下图的提示:


其中,Http状态Code也能自由控制。


三,解决方案:

1,在RateLimitInterceptor.Java拦截器中抛出异常:

[java]  view plain  copy
  1. public class RateLimitInterceptor extends HandlerInterceptorAdapter{  
  2.   
  3.     @Autowired private RedisService rs;  
  4.   
  5.     /** 
  6.      * 流量控制检查入口 
  7.      */  
  8.     @Override  
  9.     public boolean preHandle(HttpServletRequest request,  
  10.             HttpServletResponse response, Object handler) throws RequiredParameterException, SignException, RateLimitException,Exception {  
  11.         super.preHandle(request, response, handler);  
  12.         String appKey = request.getParameter("appKey");  
  13.         //判断appKey是否为空或是否合法  
  14.         if(appKey == null){  
  15.             throw new RequiredParameterException("");  
  16.         }else if(AppKeyUtils.isFormatCorrect(appKey) || !rs.isExist(appKey)){  
  17.               
  18.             throw new SignException();  
  19.               
  20.         }else {  
  21.             try {  
  22.                 AppCall appCall = AppCall.create(appKey, AppKeyUtils.getPlanDetails(appKey));  
  23.                 appCall.decrease();  
  24.                 rs.save(appCall);  
  25.                 System.out.println("RateLimitInterceptor pass.");  
  26.             } catch (RateLimitException e) {  
  27.                 //抛出超限异常  
  28.                 throw new RateLimitException();  
  29.             }  
  30.         }  
  31.         return true;  
  32.     }  
  33.   
  34. }  

当代码走到(具体怎样走到,需考虑具体业务逻辑,上述代码使用AppCall类来封装,这是后话)

[java]  view plain  copy
  1. throw new SignException();  

时,Spring将自动捕获这个异常。然后做一些处理。这是正常的流程。那么Spring如何自动捕获


2,使用Spring MVC的@ControllerAdvice,在GlobalExceptionHandler.java类中实现全局的异常处理类:

[java]  view plain  copy
  1. @ControllerAdvice  
  2. public class GlobalExceptionHandler {  
  3.   
  4.     @ExceptionHandler(SQLException.class)  
  5.     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)  
  6.     @ResponseBody  
  7.     public ExceptionResponse handleSQLException(HttpServletRequest request, Exception ex) {  
  8.         String message = ex.getMessage();  
  9.         return ExceptionResponse.create(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);  
  10.     }  
  11.        
  12.     @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="IOException occured")  
  13.     @ExceptionHandler(IOException.class)  
  14.     @ResponseBody  
  15.     public void handleIOException(){  
  16.         //returning 404 error code  
  17.     }  
  18.       
  19.     @ResponseStatus(HttpStatus.BAD_REQUEST)  
  20.     @ResponseBody  
  21.     @ExceptionHandler(SignException.class)  
  22.     public ExceptionResponse signException(SignException ex) {  
  23.         return ex.getEr();  
  24.     }  
  25.   
  26. }  

在方法的头上注解:
[java]  view plain  copy
  1. @ExceptionHandler(SignException.class)  

即表示让Spring捕获到所有抛出的SignException异常,并交由这个被注解的方法处理。

注解:

[java]  view plain  copy
  1. @ResponseBody  

即表示返回的对象,Spring会自动把该对象进行json转化,最后写入到Response中。

注解:

[java]  view plain  copy
  1. @ResponseStatus(HttpStatus.BAD_REQUEST)  

表示设置状态码。如果应用级别的错误,此处其实永远返回200也是可以接受的,但是要在你自定义的异常串和异常码中做好交代。

本文的方案自定义了一个ExceptionResponse.java类,如下:

[java]  view plain  copy
  1. /** 
  2.  * 返回的json数据 
  3.  * @author craig 
  4.  * 
  5.  */  
  6. public class ExceptionResponse {  
  7.       
  8.     private String message;  
  9.     private Integer code;  
  10.       
  11.     /** 
  12.      * Construction Method 
  13.      * @param code 
  14.      * @param message 
  15.      */  
  16.     public ExceptionResponse(Integer code, String message){  
  17.         this.message = message;  
  18.         this.code = code;  
  19.     }  
  20.       
  21.     public static ExceptionResponse create(Integer code, String message){  
  22.         return new ExceptionResponse(code, message);  
  23.     }  
  24.       
  25.     public Integer getCode() {  
  26.         return code;  
  27.     }  
  28.     public String getMessage() {  
  29.         return message;  
  30.     }  
  31.       
  32. }  

如你所知,这个类就是最后传到用户面前的一个异常类,code和message根据业务定义并让用户知晓。而自定义的SignException.java实际上起到了一个桥梁的作用。Spring把SignException对象捕获到,转成相应的ExceptionResponse对象,剩下的就是如何优雅实现的问题了。 如下是SignException.java的实现:

[java]  view plain  copy
  1. /** 
  2.  * 签名异常 
  3.  * @author tuxiao.czz 
  4.  * 
  5.  */  
  6. public class SignException extends Exception {  
  7.   
  8.     private static final long serialVersionUID = 4714113994147018010L;  
  9.     private String message = "AppKey is not correct, please check.";  
  10.     private Integer code = 10002;  
  11.           
  12.     private ExceptionResponse er;  
  13.       
  14.     public SignException() {  
  15.         er = ExceptionResponse.create(code, message);  
  16.     }  
  17.       
  18.     public ExceptionResponse getEr() {  
  19.         return er;  
  20.     }  
  21.       
  22. }  

所有关于这个异常的code和message都写在这个类里,个人感觉还是可以接受。当然还有另外一种实现,就是只拦截、定义一种Exception类,然后传不同的code和message进去,然后做相应的处理。这些都是比较灵活
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值