范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

springboot项目中简单的统一异常处理

  刚工作时,接触的第一个项目是一个新开发的运维系统,该项目是springboot 框架,也按照控制层(controller),业务层(service),数据层(dao)的结构进行的开发。而进入到开发中,难免会遇到许多业务异常,和运行时异常需要处理。那时候,项目没有统一的返回实体包装数据,返回的数据结构很随意,甚至很多异常都不会去处理,任由错误经框架抛出,很是粗暴。
  后续开发过程中,意识到任由异常抛出,这样不妥,且前端需要"优雅的"展示异常信息。于是开始采用 try catch 捕获异常后,获取异常信息,赋给异常字段后返回给前端。甚至图方便直接 controller 里面直接获取。@Controller public class AutoController {      @Autowired     private UserInfoService userInfoService;      @RequestMapping("/test")     @ResponseBody     public ResultDto test(){         try{             String name = userInfoService.getName();             ResultDto resultDto = new ResultDto();             resultDto.setData(name);             return resultDto;         }catch (Exception e){             ResultDto resultDto = new ResultDto();             resultDto.setErrorInfo(e.getMessage());             return resultDto;         }              }
  这种方式简直折磨人,每一个controller 都搞一堆 try catch 。而且,业务层的代码,遇到问题也是new 一个对象处理,后面当我接触老的项目的时候,我发现很多异常都是这么处理的,着实让人崩溃。ResultDto resultDto = new ResultDto(); resultDto.setErrorInfo("参数错误");
  后面的工作中认识到,其实项目中的统一异常处理真的很简单创建一个统一返回包装类package com.chinamobile.cmss.dmp.deployer.common.response;  import com.chinamobile.cmss.dmp.deployer.common.response.HttpStatusJsonSerializer; import com.chinamobile.cmss.dmp.deployer.exception.BusinessException; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity;  /**  * @description:  * @date: 2020/11/12/16:05  */ @AllArgsConstructor @NoArgsConstructor @Builder @Data public class ApiResponse {      /**      * http状态码      */     @JsonSerialize(using = HttpStatusJsonSerializer.class)     private HttpStatus status;     /**      * 系统错误码      */     private String code;     /**      * 提示信息,展示给用户      */     private String message;     /**      * 调试信息      */     private Object debugInfo;     /**      * 响应主体      */     private Object content; }
  2.创建一个包装返回实体的工具类,定义一些成功返回的方法。package com.chinamobile.cmss.dmp.deployer.common.response;  import org.springframework.http.HttpStatus;  /**  * 封装统一返回类  *  * @author  */ public class Response {      /**      * 具有返回数据的封装      *      * @param content 数据内容      * @return ApiResponse      */     public static ApiResponse invokeSuccess(Object content){         ApiResponse apiResponse = ApiResponse.builder()                 .status(HttpStatus.OK)                 .code(SystemCode.OK)                 .message("请求成功")                 .content(content)                 .build();         return apiResponse;     }      /**      * 没有返回数据的封装      *      * @return ApiResponse      */     public static ApiResponse invokeSuccess(){         ApiResponse apiResponse = ApiResponse.builder()                 .status(HttpStatus.OK)                 .code(SystemCode.OK)                 .message("请求成功")                 .content(null)                 .build();         return apiResponse;     }      /**      * @param content 返回数据      * @param message 消息      * @return ApiResponse      */     public static ApiResponse invokeSuccess(Object content,String message){         ApiResponse apiResponse = ApiResponse.builder()                 .status(HttpStatus.OK)                 .code(SystemCode.OK)                 .message(message)                 .content(content)                 .build();         return apiResponse;     }      /**      *      * @param status 请求状态      * @param code 状态码      * @param content 返回内容      * @param message 消息      * @return ApiResponse      */     public static ApiResponse invoke(HttpStatus status, String code, Object content, String message){         ApiResponse apiResponse = ApiResponse.builder()                 .status(status)                 .code(code)                 .message(message)                 .content(content)                 .build();         return apiResponse;     }      /**      *      * @param status 请求状态      * @param code 状态码      * @param message 消息      * @return ApiResponse      */     public static ApiResponse invoke(HttpStatus status, String code, String message){         ApiResponse apiResponse = ApiResponse.builder()                 .status(status)                 .code(code)                 .message(message)                 .build();         return apiResponse;     } }
  3.创建一个定义错误信息的枚举,定义业务错误信息package com.li.core.hellomeeting.common.Response;  /**  * 返回码和返回消息  *  */ public enum ResponseCodeEnum {      SUCCESS(200,"请求成功"),      INTERNAL_SERVER_ERROR(500,"服务器内部错误"),      /** 参数错误 **/     PARAM_INVALID_ERROR(1001,"参数校验错误"),      USER_NOTE_EXIST(2004,"用户不存在"),      FORBIDDEN(403,"禁止访问"),      UNAUTHORIZED(401,"未登录");      private Integer code;      private String message;      ResponseCodeEnum(Integer code, String message) {         this.code = code;         this.message = message;     }      public Integer getCode() {         return code;     }      public void setCode(Integer code) {         this.code = code;     }      public String getMessage() {         return message;     }      public void setMessage(String message) {         this.message = message;     }  }
  3.创建一个业务异常类,继承 RuntimeException 类package com.li.core.hellomeeting.exception;  import com.li.core.hellomeeting.common.Response.ResponseResult;  /**  * 统一业务异常  *  */ public class BusinessException extends RuntimeException{      private ResponseResult responseResult;      public BusinessException(String message) {         super(message);     }      public BusinessException(String message, Throwable cause) {         super(message, cause);     }      public BusinessException(Throwable cause) {         super(cause);     }      public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {         super(message, cause, enableSuppression, writableStackTrace);     }      public ResponseResult getResponseResult() {         return responseResult;     }      public void setResponseResult(ResponseResult responseResult) {         this.responseResult = responseResult;     } }
  4.创建一个异常捕获处理类,使用 @RestControllerAdvice 注解,拦截抛出的异常。package com.li.core.hellomeeting.exception;  import com.li.core.hellomeeting.common.Response.ErrorInfo; import com.li.core.hellomeeting.common.Response.ResponseResult; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; import org.springframework.validation.BindException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.multipart.MultipartException;  import java.util.List; import java.util.stream.Collectors;  import static com.li.core.hellomeeting.common.Response.StatusCode.*; import static com.li.core.hellomeeting.common.Response.StatusCode.PERMISSION_NO_ACCESS;   /**  * 全局异常拦截  *  * @author  */ @Slf4j @RestControllerAdvice public class GlobalExceptionHandler {      private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);      /**      * 参数校验错误      *      * @param e 错误信息      * @return ResponseResult      */     @ExceptionHandler(value = BindException.class)     public ResponseResult handleBindException(BindException e){         log.error("参数校验错误", e);         ErrorInfo errorInfo = new ErrorInfo();         List fieldErrorInfos = e.getFieldErrors().stream()                 .map(fieldError -> new ErrorInfo.FieldErrorInfo(fieldError.getField(),fieldError.getRejectedValue(),fieldError.getDefaultMessage()))                 .collect(Collectors.toList());         errorInfo.setFieldErrorInfos(fieldErrorInfos);         return ResponseResult.builder()                 .code(PARAM_IS_INVALID.code())                 .message(PARAM_IS_INVALID.message())                 .flag(PARAM_IS_INVALID.status())                 .build();     }      /**      *处理参数错误信息      *      * @param e 错误信息      * @return ResponseResult      */     @ExceptionHandler(value = MethodArgumentNotValidException.class)     public ResponseResult handleBindException(MethodArgumentNotValidException e) {         log.error("参数错误{}", e);         return ResponseResult.builder()                 .code(PARAM_IS_INVALID.code())                 .message(PARAM_IS_INVALID.message())                 .flag(PARAM_IS_INVALID.status())                 .build();     }      /**      *处理非法的请求方法错误      *      * @param e 错误信息      * @return ResponseResult      */     @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)     public ResponseResult handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {         String msg = String.format("不支持 [%s] 请求", e.getMethod());         log.error(msg+"{}", e);         return ResponseResult.builder()                 .code(METHOD_NOT_ALLOWED.code())                 .message(METHOD_NOT_ALLOWED.message())                 .flag(METHOD_NOT_ALLOWED.status())                 .build();     }      /**      * 处理参数缺失错误信息      *      * @param e 错误信息      * @return ResponseResult      */     @ExceptionHandler(value = MissingServletRequestParameterException.class)     public ResponseResult handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {         String msg = String.format("缺少 %s 参数", e.getParameterName());         log.error(msg+"{}", e);         return ResponseResult.builder()                 .code(PARAM_NOT_COMPLETE.code())                 .message(PARAM_NOT_COMPLETE.message())                 .flag(PARAM_NOT_COMPLETE.status())                 .build();     }      /**      * 处理上传异常信息      *      * @param e 错误信息      * @return ResponseResult      */     @ExceptionHandler(value = MultipartException.class)     public ResponseResult handleMultipartException(MultipartException e) {         log.error("上传异常{}", e);         return ResponseResult.builder()                 .code(EXCEED_MAX_SIZE.code())                 .message(EXCEED_MAX_SIZE.message())                 .flag(EXCEED_MAX_SIZE.status())                 .build();     }      /**      * 处理访问权限错误      *      * @param e 错误信息      * @return ResponseResult      */     @ResponseBody     @ExceptionHandler(AccessDeniedException.class)     public ResponseResult handleAccessDeniedException(AccessDeniedException e) {         return ResponseResult.builder()                 .code(PERMISSION_NO_ACCESS.code())                 .message(PERMISSION_NO_ACCESS.message())                 .flag(PERMISSION_NO_ACCESS.status())                 .build();     }      /**      * 处理全局异常      * 通常处理统一的未知异常      *      * @param e 异常信息      * @return ResponseResult      */     @ExceptionHandler(value = Exception.class)     public ResponseResult handleException(Exception e) {         log.error("服务器内部错误", e);         return ResponseResult.builder()                 .code(INTERNAL_SERVER_ERROR.code())                 .message(INTERNAL_SERVER_ERROR.message())                 .flag(INTERNAL_SERVER_ERROR.status())                 .build();     }      /**      * 处理业务异常      * 如果携带了一个 ResponseResult,则提取出来进行返回      * 否则返回 null      *      * @param e 错误信息      * @return ResponseResult      */     @ExceptionHandler(value = BusinessException.class)     public ResponseResult handleBusinessException(BusinessException e) {         log.error(e.getMessage(), e);         ResponseResult responseResult;         if(e.getResponseResult() != null){             responseResult = e.getResponseResult();         }else{             log.error("服务器内部错误{}", e.getCause());             responseResult = ResponseResult.builder()                     .code(INTERNAL_SERVER_ERROR.code())                     .message(INTERNAL_SERVER_ERROR.message())                     .flag(INTERNAL_SERVER_ERROR.status())                     .build();         }         return responseResult;     }   }
  5.封装一些异常错误,配合定义好的错误枚举类使用。package com.chinamobile.cmss.dmp.deployer.exception;  import com.chinamobile.cmss.dmp.deployer.common.response.Response; import com.chinamobile.cmss.dmp.deployer.common.response.SystemCode; import org.springframework.http.HttpStatus;  /**  * 异常统一抛出封装  *  */ public class MyException {      /**      *抛出错误信息      *      * @param status 状态      * @param code 状态码      * @param content 内容      * @param message 错误信息      * @return BusinessException      */     public BusinessException throwException(HttpStatus status, String code, Object content, String message){         BusinessException businessException = new BusinessException(message);         businessException.setApiResponse(Response.invoke(status,code,content,message));         return businessException;     }      /**      * 参数错误异常      * @param message 异常信息      * @return BusinessException      */     public BusinessException badRequest(String message){         BusinessException businessException = new BusinessException(message);         businessException.setApiResponse(Response.invoke(HttpStatus.BAD_REQUEST, SystemCode.BAD_REQUEST,null,message));         return businessException;     }      /**      * 找不到目标值错误      *      * @param message 错误信息      * @return BusinessException      */     public BusinessException notFound(String message){         BusinessException businessException = new BusinessException(message);         businessException.setApiResponse(Response.invoke(HttpStatus.NOT_FOUND, SystemCode.NOT_FOUND,null,message));         return businessException;     }      /**      * 无权限错误      *      * @param message 错误信息      * @return BusinessException      */     public BusinessException forbideen(String message){         BusinessException businessException = new BusinessException(message);         businessException.setApiResponse(Response.invoke(HttpStatus.FORBIDDEN,SystemCode.FORBIDDEN,null,message));         return businessException;     } }
  在业务中使用,截取个例子一部分代码@Override     public void deployService(DeployParam deployParam){         log.info("------> 【自动化部署接口】参数:{}",deployParam);         //校验一下         checkParam(deployParam);         //获取环境配置信息         ServiceDeployInfoDto serviceDeployInfoDto = getServiceDeployInfoDto(deployParam);         log.info("------> 整合环境配置信息:{}",serviceDeployInfoDto);         //判断服务是否在部署中         boolean status = DeployUtils.isDeploying(deployParam.getEnv(),deployParam.getInstance());         log.info("------> 判断当前实例是否正在部署,status: {}",status);         if(status){             log.error("{} 服务正在部署中,请稍后再部署",deployParam.getInstance());             throw new MyException().badRequest("该服务正在部署中,请稍后");         }         //2.开始部署         log.info("------>  开始进行异步部署......");         if(deployParam.getInstance().equals("openapi2")){             serviceDeployInfoDto.setInstanceName("openapi");         }         deployManager.deployService(serviceDeployInfoDto);     }
  在controller 中使用@PostMapping(value = "/deploy")     public ResponseEntity deploy(@RequestParam("file") MultipartFile file,@Validated DeployParam deployParam,BindingResult result){         ControllerUtils.checkBindingResult(result);         deployParam.setFile(file);         deployService.deployService(deployParam);         return ResponseEntity.ok(Response.invokeSuccess());     }
球王会体育破晓杯TT实力不容小觑,TQ遗憾落败球王会体育英雄联盟手游破晓被一直深受众人的期待,而在11月13号的时候,这一场赛事终于是拉开了序幕,在11月16号的时候,我们能够看到TT与TQ的一场精彩对决,不得不说的是这一场赛战地204211。19发售,致敬玩家的3A新作品质如何?战地2042还未正式发售就已经斩获了Steam周销量榜第一,经历了多次跳票后,战地2042已于11月19日发售。一款还没发售的游戏能斩获周销榜第一,战地2042有着怎样的魔力?一切王者荣耀玩家认为,夏侯惇的弱势在于大招位移过长,这种说法你认同吗?hello大家好,我是可爱且魅力四射,迷人但不失风度的熊猫。夏侯惇这名英雄在游戏当中呢还算是一名不错的英雄吧,我认为强度一直都在中上,并不算是弱势!夏侯惇有肉有输出,在团战当中完全打BOSS不只靠武器?幻塔战斗高端技巧,忽略了它们少一半输出但凡是玩游戏,基本都会对打BOSS很熟悉,它考验着玩家的战斗技巧和输出能力,而武器又是影响输出能力的重要因素。这也就导致大多数游戏产品在武器上花费了不少心思,对应的玩家也记住了诸如金蝉能否成为上位法师一哥?诸葛亮被削,嬴政是最大的对手令人期待的金蝉,终于出现在了游戏中,定位为法师和辅助的英雄,毫无疑问,他也会成为最近一段时间,中路最常出现的英雄,那么他最后在法师排名中会处于什么位置呢?诸葛亮惨遭削弱,干将只能望打王者荣耀被人骂的最难听的话是什么?没有图片,没截屏我一个女生,记得最清楚的有两次玩妲己,逆风局,快输了,本来想蹲对面残血打野,蹲到了狄仁杰,击杀,然后再次蹲草,结果又是狄仁杰,然后他就开始说话了,第一句我不记得是什国服中单被逼转型?北枫男刀打野,无解支援称霸峡谷之巅为什么现在绝活哥主播的处境越来越不好,段位越高越会被克制拿不到自己拿手英雄,在峡谷之巅有很多的绝活哥但一天下来玩不了多少自己的拿手英雄,所以很多主播都被迫练了很多的英雄,但是英雄熟中日韩主播对抗赛含金量十足?格局遗憾独一档,天灰灰打出风采近日,伴随着中日韩亚洲对抗赛的圆满落幕,因为是赛区与赛区主播之间的对决,来自中国赛区的两支主播队伍DYU与DYD成功在决赛中分别击败了猪让队与核皇队的韩国队伍,帮助中国主播队夺得了2021年DNF职业联赛老盟主仇东升夺得冠军神佛难挡,从败者组一路打败7人,夺得冠军!在2021DNF职业联赛中,中国选手仇东升越打越猛,从败者组一路打败7人,夺得冠军,奠定了目前中韩pk第一人的宝座。在此之前,仇东升因为作Fami通一周销量榜出炉生化危机8二次登顶Fami通公开了最新一周(5月10日5月16日)日本游戏软硬件销量榜,软件方面,生化危机8村庄第二次登顶榜首,同为卡普空旗下的怪物猎人崛起位居第二,上周榜首New宝可梦随乐拍降至第Uzi和Gala闪现反应对比图火了,大嘴一赶四,RNG和我没关系了前言S11赛季的比赛正在如火如荼地进行中,相信绝大多数的玩家都关注了,最近一段时间的Msi。Rng战队作为lpl赛区春季赛的冠军,整体的状态和发挥是非常出色的,在目前的比赛中已经拿
战舰世界新年玩法升级,超多新年福利等你来拿2021已经与我们分别,新的一年已经来临。在新的一年里战舰世界带来了一系列重磅内容,爽翻了一众玩家。首先就是2021年刚刚开启没多久的全球同步,0。10。11版本中,研发局,造船厂缤纷独角兽和乘风破浪将上架碎片商城,你攒够皮肤碎片了嘛?小伙伴们,元旦快乐呀!最近体验服更新的内容显示,碎片商城出现了夏日系列的史诗皮肤,分别是夏侯惇乘风破浪李元芳逐浪之夏以及小乔缤纷独角兽没想到等了这么久的乘风破浪和独角兽再次上架碎片云中赛年第二名英雄确定,手持长剑性别女,暃伴生皮肤只需23碎片对于云中赛年即将就要开启这点,相信绝大部分玩家应该都是知晓的。并且,对于云中赛年的第一名新英雄,相信玩家应该也都不陌生。毕竟,暃这名英雄近期可是被官方公布出来了很多的相关,甚至对于光明记忆无限Steam热卖华硕Z690主板带你全效畅玩国产游戏的蓬勃发展有目共睹,不仅在国内受人追捧,国际上也大受欢迎。在2021Steam大奖中,国内游戏开发者飞燕群岛的最新作品科幻FPS光明记忆无限,就获得了杰出视觉风格的提名。该2位新英雄官宣,建模剪影公布,暃伴生皮爆料,备好23碎片前言新赛季的新皮肤爆料基本结束了,除了新英雄暃的伴生特效尚未官宣外,基本上是否需要提前准备点券心里都有预期了,而正式服的新英雄联动活动中,提前爆料了暃伴生皮肤的名称以及相应的获取福王者S26更新时间,战令皮肤曝光,新英雄暃,新增两大段位新鲜事我来报s26赛季更新时间确定,达摩新皮肤免费领,S26战令皮肤皮肤全部曝光,1级战令皮肤芈月幻夜卜梦,而80级战令皮肤则给了老将黄忠,然后就是战令星元皮肤,上次给了亚瑟,这次超级人类延长封测时间新手怎么注册游戏原本定于27日结束本轮测试的Superpeople宣布延长此次的测试时间,具体结束时间待定。这一消息,获得了玩家们的大力支持,因为有不少玩家表示,自己已经离不开超级人类这款游戏了。奇幻养成恐怖巧克力工厂新截图年内公布更多消息奇幻养成游戏恐怖巧克力工厂(HauntedChocolatier)是ConcernedApe继星露谷物语之后的又一力作,母亲啊游戏已开发一年多,但登陆平台和大致发售区间都未公布。随部落与弯刀隐藏的强力NPC不要错过,长相虽丑,但能力堪比T0众所周知,部落与弯刀这款游戏看名字就知道,部落才是最重要的,个人能力再强,也敌不过一个强大的部落,虽然是单机游戏,但是作为开放式沙盒游戏,部落与弯刀里面的部落属性显得尤为重要!新手梦幻模拟战手游盘点pve中各位司机的表现和能力作者NGA炼金术士玛莉开车司机盘点(pve向),要是有漏的欢迎指出,我再补。老玩家基本心里门清,就不要吐槽内容太人所共知了。战斗力分类解说1级完全工具人,开完车就只能干瞪眼,打人也宝藏女孩林七七魅力无限!Uzi借故查房,Letme不能淡定了平常喜欢观看虎牙直播的小伙伴们,应该对近日一位英雄联盟女主播有所了解,她便是林七七。这段时间里,林七七在英雄联盟直播里可以说深受欢迎,另外早在之前她还曾经担当过二路英雄联盟主持人。