先创建一个crud的项目。

controller调用service调用mapper

以下以简单代码代替

  • controller
  1. @GetMapping("/getUserById")
  2. public String getUserById(String id){
  3. String userById = userService.getUserById(id);
  4. return userById;
  5. }
  • service
  1. @Override
  2. public String getUserById(String id) {
  3. // 模拟业务
  4. User user = userMapper.selectById(id);
  5. return user.toString();
  6. }

上面代码虽然是把数据返回给前台了,但是没有处理异常以及没有一个明确的标识告诉前端我是成功还是失败了,此时我们需要封装一下统一的成功失败标志来告诉前台如何处理返回数据。

使用Lombok 简单封装了一个CommonResponse类(lombok的使用需要考虑项目是否真正适合lombok,不要因为方便引入导致后期版本或者一些其他的隐藏坑。)

  1. @Data
  2. public class CommonResponse {
  3. /**
  4. * 返回业务码用来判断成功失败
  5. * 200 成功
  6. * 500 失败
  7. */
  8. private String code;
  9. /** 描述 */
  10. private String massage;
  11. /** 描述 */
  12. private Object date;
  13. public CommonResponse(String code, String massage, Object date) {
  14. this.code = code;
  15. this.massage = massage;
  16. this.date = date;
  17. }
  18. public static CommonResponse succeed(){
  19. return getCommonResponse(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMassage(), null);
  20. }
  21. public static CommonResponse succeed(Object date){
  22. return getCommonResponse(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMassage(), date);
  23. }
  24. public static CommonResponse succeed(String massage,Object date){
  25. return getCommonResponse(CodeEnum.SUCCESS.getCode(), massage, date);
  26. }
  27. public static CommonResponse error(String massage){
  28. return getCommonResponse(CodeEnum.ERROR.getCode(), massage, null);
  29. }
  30. public static CommonResponse error(String code,String massage){
  31. return getCommonResponse(code, massage, null);
  32. }
  33. public static CommonResponse error(){
  34. return getCommonResponse(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMassage(), null);
  35. }
  36. public static CommonResponse getCommonResponse(String code, String massage, Object date){
  37. return new CommonResponse(code,massage,date);
  38. }
  39. }

返回的controller使用统一的CommonResponse

  1. @GetMapping("/getUserById")
  2. public CommonResponse getUserById(String id){
  3. String userById = userService.getUserById(id);
  4. return CommonResponse.succeed(userById);
  5. }

返回

  1. {
  2. "code": "200",
  3. "massage": "成功",
  4. "date": "User(id=1, username=嘉文00, password=1000000, age=5555)"
  5. }

上述返回基本符合预期,但是当程序出现未知异常怎么办了。

对service改造下

  1. @Override
  2. public String getUserByIdException(String id) {
  3. User user = userMapper.selectById(id);
  4. // 模拟业务异常
  5. int i=5/0;
  6. return user.toString();
  7. }

controller 改造

  1. @GetMapping("/getUserById")
  2. public CommonResponse getUserById(String id){
  3. try{
  4. String userById = userService.getUserById(id);
  5. return CommonResponse.succeed(userById);
  6. }catch(Exception e){
  7. e.printStackTrace();
  8. log.error(e.getMessage());
  9. return CommonResponse.error(e.getMessage());
  10. }
  11. }

上面是不是也可以,通过trycatch来判断如何返回。但是这样代码中就会出现大量的try catch,是不是很不好看,我们能不能添加一个统一的try呢,答案是可以的。

使用spring提供的统一的异常处理

spring 提供了三种异常捕获方式,个人比较推荐这一种

  1. @Slf4j
  2. @ControllerAdvice
  3. public class ExceptionHandle {
  4. /**
  5. * 处理未知异常
  6. * @param e
  7. * @return
  8. */
  9. @ExceptionHandler(Exception.class)
  10. @ResponseBody
  11. public CommonResponse handleException(Exception e){
  12. log.error("系统异常:{}",e.getMessage());
  13. return CommonResponse.error(e.getMessage());
  14. }
  15. /**
  16. * 处理主动抛出的自定义异常
  17. * @param e
  18. * @return
  19. */
  20. @ExceptionHandler(BusinessException.class)
  21. @ResponseBody
  22. public CommonResponse handleBusinessException(BusinessException e){
  23. log.error("自定义异常:{}",e.getErrMassage());
  24. return CommonResponse.error(e.getErrCode(),e.getErrMassage());
  25. }
  26. }

踩过的坑

  1. 这个地方在写的时候遇到一个坑,因为捕获异常后的返回值是CommonResponse,所以要加上注解 @ResponseBody 便于 格式转换。
  2. 在配置@ExceptionHandler的时候不能配置两个相同的Exception。否则会不知道使用哪个而报错。

这时controller 已经很清晰了,如下:只用处理好业务调用,无需处理向上抛出的异常。

  1. @GetMapping("/getUserByIdException")
  2. public CommonResponse getUserByIdException(String id){
  3. String userById = userService.getUserByIdException(id);
  4. return CommonResponse.succeed(userById);
  5. }
  6. @GetMapping("/getUserByIdBusinessException")
  7. public CommonResponse getUserByIdBusinessException(String id){
  8. String userById = userService.getUserByIdBusinessException(id);
  9. return CommonResponse.succeed(userById);
  10. }

当然有时候我们会遇到自己的校验不通过来终止程序。我们可以throw 一个Exception 或者我们需要定制返回码,自定义一个异常类也行。如下简单示例,大家可以根据自己的业务需求去自定义。

  • 自定义异常类BusinessException
  1. @Data
  2. public class BusinessException extends RuntimeException{
  3. private static final long serialVersionUID = 918204099850898995L;
  4. private String errCode;
  5. private String errMassage;
  6. public BusinessException(String errCode,String errMassage){
  7. super(errMassage);
  8. this.errCode = errCode;
  9. this.errMassage = errMassage;
  10. }
  11. }
  • service返回自定义异常
  1. @Override
  2. public String getUserByIdBusinessException(String id) {
  3. User user = userMapper.selectById(id);
  4. // 模拟业务异常
  5. if("1".equals(id)){
  6. throw new BusinessException("400","id为1的数据不支持查询。");
  7. }
  8. return user.toString();
  9. }

此时我们前端得到的返回

  1. ## 请求
  2. http://localhost:8088/getUserByIdBusinessException?id=1
  3. ## 返回
  4. {
  5. "code": "400",
  6. "massage": "id为1的数据不支持查询。",
  7. "date": null
  8. }

以上就是统一异常捕获跟统一的返回。

另外我们实际项目为了使业务异常好看,统一,我们可以定义一个枚举来存放我们的业务异常信息。

  1. 定义一个枚举类
  1. public enum ExceptionEnum {
  2. BUSINESS_NOT_ONE("400","id为1的数据不支持查询"),
  3. ERR_NOT_LOOK("401","长得太帅不让看")
  4. // 往后面累加...
  5. ;
  6. private String code;
  7. private String desc;
  8. ExceptionEnum(String code, String desc) {
  9. this.code = code;
  10. this.desc = desc;
  11. }
  12. public void ThrowException(){
  13. ThrowException(code,desc);
  14. }
  15. public void ThrowException(String errMassage){
  16. errMassage = desc +":"+errMassage;
  17. ThrowException(code,errMassage);
  18. }
  19. private BusinessException ThrowException(String code,String desc){
  20. throw new BusinessException(code,desc);
  21. }
  22. }
  1. 在service 中抛出枚举异常
  1. @Override
  2. public String getUserByIdBusinessExceptionByEnumOne(String id) {
  3. User user = userMapper.selectById(id);
  4. // 模拟业务异常
  5. if("1".equals(id)){
  6. ExceptionEnum.BUSINESS_NOT_ONE.ThrowException();
  7. }
  8. return user.toString();
  9. }
  10. @Override
  11. public String getUserByIdBusinessExceptionByEnumTwo(String id) {
  12. User user = userMapper.selectById(id);
  13. // 模拟业务异常
  14. if("look".equals(id)){
  15. // 可以动态拼接异常信息
  16. ExceptionEnum.ERR_NOT_LOOK.ThrowException("你说对吧"+id);
  17. }
  18. return user.toString();
  19. }
  1. 前台返回

  1. {
  2. "code": "400",
  3. "massage": "id为1的数据不支持查询",
  4. "date": null
  5. }
  6. {
  7. "code": "401",
  8. "massage": "长得太帅不让看:你说对吧look",
  9. "date": null
  10. }

这种做法的好处就是方便管理,一眼就知道自己项目中有多少错误,多少异常。但是也会有同学觉得这样写好费劲,每次抛异常的时候还要先在枚举类中定义。所以这个做法是项目跟成员而定。

以上是本人拙见,若有不合适之处,欢迎留言指正。

从项目中来到项目中去

Java项目常用的统一返回跟统一异常处理的更多相关文章

  1. java项目常用 BaseDao BaseService

    java项目常用 BaseDao BaseService IBaseDao 1 package com.glht.sim.dao; 2 3  import java.util.List; 4 5 6 ...

  2. Java基础学习总结(70)——开发Java项目常用的工具汇总

    要想全面了解java开发工具,我们首先需要先了解一下java程序的开发过程,通过这个过程我们能够了解到java开发都需要用到那些工具. 首先我们先了解完整项目开发过程,如图所示: 从上图中我们能看到一 ...

  3. Spring Boot 统一返回结果及异常处理

    在 Spring Boot 构建电商基础秒杀项目 (三) 通用的返回对象 & 异常处理 基础上优化.调整 一.通用类 1.1 通用的返回对象 public class CommonReturn ...

  4. java项目常用架构

    三层架构 : 界面层/表现层 UI 业务逻辑层 BLL 针对具体的问题的操作,也可以理解成对数据层的操作,对数据业务逻辑处理. 数据访问层 DAL 访问数据库 mvc : 而 MVC 是在三层架构的基 ...

  5. JAVA项目常用异常处理情况

    Java异常处理 网络整理 这里是异常的说明: 算术异常类:ArithmeticExecption 空指针异常类:NullPointerException 类型强制转换异常:ClassCastExce ...

  6. Java 项目创建 -- 统一结果处理、统一异常处理、统一日志处理

    一.IDEA 插件使用 1.说明 此处使用 SpringBoot 2.2.6 .JDK 1.8 .mysql 8.0.18 作为演示. 使用 IDEA 作为开发工具. 2.IDEA 插件 -- Lom ...

  7. [原创]Java项目统一UTC时间方案

    Java项目统一UTC时间方案 作者:Gods_巨蚁 引言 近期团队的个别项目在进行框架升级后,部分时间值存在8小时误差,原因是错误的将数据库中的时间数据理解成了UTC时间(旧版本认为是北京时间) 考 ...

  8. java统一返回标准类型

    一.前言.背景 在如今前后端分离的时代,后端已经由传统的返回view视图转变为返回json数据,此json数据可能包括返回状态.数据.信息等......因为程序猿的习惯不同所以返回json数据的格式也 ...

  9. Java封装接口统一返回数据模板

    现在大多数都使用前后端分离开发模式,前端通过Ajax请求访问后台服务器,后台返回JSON数据供前端操作,这里编写一个统一返回数据模板类,方便日后操作 public class R extends Ha ...

随机推荐

  1. 《3D打印与工业制造》—— 读书笔记

    <3D打印与工业制造>-- 读书笔记 原创内容,学习不易,转载请注明出处! 一.读后感-- "WOW" ​ 可以这么说,<3D打印与工业制造>这本书是我第一 ...

  2. leetcode 最佳买卖股票时机含冷冻期

    这道题算是股票问题的变体之一,主要在于不限制交易次数而存在冷冻期,所以我们需要对我们的dp数组进行改变,第一维是指第几天,第二维是指是否持有股票,在这里因为不限制交易次数k,所以并未涉及第三维度. 同 ...

  3. Tomcat配置SSL证书(PFX证书)

    公司项目,应该是阿里云服务器 在windows2008 R2搭建的 Tomcat部署SSL证书,本文以PFX证书为例. 配置好之后开始 一.什么是SSL(证书)? SSL证书服务(Alibaba Cl ...

  4. zookeeper同一台服务器创建伪集群

    下载zk wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.7.0/apache-zookeeper-3.7 ...

  5. 图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)

    参考网址:图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS) - 51CTO.COM 深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath ...

  6. mysql 局域网连接

    下面分别简述操作: 配置虚拟机网络 默认方式是NAT,但为了让宿主机之外的其它计算机也能访问虚拟机,NAT方式配置起来有些复杂,这里推荐用桥接模式,关于VM的几种网络方式的区别,可以参考这篇文章配置好 ...

  7. Java File常见用法

    一.构造方法 File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例. File(String pathname) 通过将给定的路径名字符 ...

  8. EL表达式学习(二)

    1.从特定域中获取值: 2.从请求页面的input标签中,获取值:(同servlet中的getParameter和getParameterValues): 3.获取请求头(同servlet中的getH ...

  9. linux centos 网卡有关调试

    本文章出至于设置固定IP不起作用,名称也无法修改 1.设置网卡配置文件 打开文件 vi /etc/sysconfig/network-scripts/ifcfg-enp8s0 把NAME.DEVICE ...

  10. Win10 pip install augimg 报 OSError: [WinError 126] 找不到指定的模块,解决办法

    第一种Win10下python成功安装augimg的方法: 下载Shapely,地址https://www.lfd.uci.edu/~gohlke/pythonlibs/#shapely,选择对应版本 ...