java
主页 > 软件编程 > java >

Spring Boot统一接口返回及全局异常处理

2022-04-20 | 秩名 | 点击:

前言:

前段时间接手了一个老项目,现在需要在此项目中添加一些新的需求,同事在开发过程中遇到了一些问题?

其实这些问题,归根究底还是代码规范问题,我们需要将接口定义和全局异常统一处理,历史项目10多个工程,难道每个工程都去实现一遍,答案可定是不可能的。

1、解决方案

定义公共模块,实现统一接口定义规范和异常处理,其他的系统进行依赖和扩展即可。

2、具体实现

2.1 定义状态码统一接口

1

2

3

4

5

6

7

8

9

10

11

12

13

public interface BaseResultCode

{

    /**

     * 状态码

     * @return

     */

    int getCode();

    /**

     * 提示信息

     * @return

     */

    String getMsg();

}

2.2 公共模块状态码枚举类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

public enum ResultCode implements BaseResultCode

{

    OK(200, "成功"),

    ERROR(300,"系统异常"),

    NEED_AUTH(301, "非法请求,请重新登录"),

    PARAMTER_ERROR(302, "参数错误");

    //省略其他定义错误码

    private int code;

    private String msg;

    private ResultCode(int code, String msg)

    {

        this.code = code;

        this.msg = msg;

    }

    public static ResultCode getValue(int code)

    {

        for (ResultCode errorCode : values())

        {

            if (errorCode.getCode() == code)

            {

                return errorCode;

            }

        }

        return null;

    }

   //省略Get、Set方法

 }

2.3 定义全局自定义异常

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

public class SysException extends RuntimeException

{

    private static final long serialVersionUID = 5225171867523879342L;

    private int code;

    private String msg;

    private Object[] params;

    private BaseResultCode errorCode;

    public SysException()

    {

        super();

    }

    public SysException(String message)

    {

        super(message);

    }

    public SysException(Throwable cause)

    {

        super(cause);

    }

    public SysException(int code ,String message)

    {

        this.code = code;

        this.msg = message;

    }

    public SysException(int code ,String message,  Object[] params)

    {

        this(code, message);

        this.params= params;

    }

    public SysException(String message, Throwable cause)

    {

        super(message, cause);

    }

    public SysException(BaseResultCode errorCode)

    {

        this.errorCode = errorCode;

    }

    public SysException(String message, Object[] params)

    {

        super(message);

        this.params = params;

    }

    public SysException(BaseResultCode errorCode, String message, Object[] params)

    {

        this(message, params);

        this.errorCode = errorCode;

    }

     

    /**

     * Construct by default

     *

     * @param message

     *            message

     * @param parameters

     *            parameters

     * @param cause

     *            cause

     */

    public SysException(String message, Object[] params, Throwable cause)

    {

        super(message, cause);

        this.params = params;

    }

 

    public int getCode()

    {

        return code;

    }

    public void setCode(int code)

    {

        this.code = code;

    }

    public String getMsg()

    {

        return msg;

    }

    public void setMsg(String msg)

    {

        this.msg = msg;

    }

    /**

     * @return the params

     */

    public Object[] getParams()

    {

        return params;

    }

    /**

     * @param params

     *            the params to set

     */

    public void setParams(Object[] params)

    {

        this.params = params;

    }

    public BaseResultCode getErrorCode()

    {

        return errorCode;

    }

    public void setErrorCode(BaseResultCode errorCode)

    {

        this.errorCode = errorCode;

    }

     

}

2.4 定义统一接口格式输出类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

public class Result implements Serializable

{

    private static final long serialVersionUID = -1773941471021475043L;

    private Object data;

    private int code;

    private String msg;

    public Result()

    {

    }

    public Result(int code, Object data, String msg)

    {

        this.code = code;

        this.data = data;

        this.msg = msg;

    }

    public Result(int code, String desc)

    {

        this(code, null, desc);

    }

    public Result(BaseResultCode errorCode)

    {

        this(errorCode.getCode(), null, errorCode.getMsg());

    }

    public static Result success()

    {

        return success(null);

    }

    public static Result success(Object data)

    {

        Result result = new Result();

        result.setData(data);

        result.setCode(ResultCode.OK.getCode());

        return result;

    }

    public static Result error(String msg)

    {

        Result result = new Result();

        result.setCode(ResultCode.ERROR.getCode());

        result.setMsg(msg);

        return result;

    }

    public static Result error(BaseResultCode baseCode)

    {

        Result result = new Result();

        result.setCode(baseCode.getCode());

        result.setMsg(baseCode.getMsg());

        return result;

    }

}

个人建议:统一接口输出类不要定义为泛型类型

2.5 定义统一接口格式输出类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

@RestControllerAdvice

public class SysExceptionHandler

{

    public static Log logger = LogManager.getLogger(SysExceptionHandler.class);

    @ExceptionHandler(Exception.class)

    public Result handleException(HttpServletRequest request,

            Exception ex)

    {

        logger.error("Handle Exception Request Url:{},Exception:{}",request.getRequestURL(),ex);

        Result result = new Result();

        //系统异常

        if (ex instanceof SysException)

        {

            SysException se = (SysException) ex;

            BaseResultCode resultCode =se.getErrorCode();

            if(resultCode==null)

            {

                result = Result.error(se.getMessage());

            }

            else

            {

               result = new Result(resultCode.getCode(),

                                       StringUtil.isNotEmpty(se.getMessage())?se.getMessage():resultCode.getMsg());

            }

        }

        //参数错误

        else if (ex instanceof ConstraintViolationException)

        {

            ConstraintViolationException v = (ConstraintViolationException) ex;

            String message = v.getConstraintViolations().iterator().next()

                    .getMessage();

            result.setCode(ResultCode.PARAMTER_ERROR.getCode());

            result.setMsg(ResultCode.PARAMTER_ERROR.getMsg() + ":" + message);

        }

        //参数错误

        else if (ex instanceof BindException)

        {

            BindException v = (BindException) ex;

            String message = v.getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(","));

            result.setCode(ResultCode.PARAMTER_ERROR.getCode());

            result.setMsg(ResultCode.PARAMTER_ERROR.getMsg() + ":" + message);

        }

        //参数错误

        else if (ex instanceof MethodArgumentNotValidException)

        {

            MethodArgumentNotValidException v = (MethodArgumentNotValidException) ex;

            String message = v.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(","));

            result.setCode(ResultCode.PARAMTER_ERROR.getCode());

            result.setMsg(ResultCode.PARAMTER_ERROR.getMsg() + ":" + message);

        }

        else

        {

           result = new Result(ResultCode.ERROR.getCode(),ExceptionUtil.getErrorMsg(ex));

        }

        logger.info("exception handle reuslt:" + result);

        return result;

    }

}

上述定义已经可以实现全局接口和异常的统一处理,但是存在的如下问题

每个controller都需要返回Reesult类型,且每个方法都需要返回Result.success()或者Result.success(data)的结果,有点重复,需要进行优化。

1

2

3

4

5

6

7

8

9

10

11

12

13

@GetMapping("addUser")

public Result add()

{

   for(int i=0;i<10;i++)

   {

       TUser user = new TUser();

       //user.setOid(IdWorker.getId());

       user.setName("shareing_"+i);

       user.setAge(i);

       userService.addUser(user);

   }

   return Result.success();

}

2.6 接口统一输出优化

实现方式只需要实现ResponseBodyAdvice接口,重写beforeBodyWrite方法接口。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

@RestControllerAdvice

public class ResponseAdvice implements ResponseBodyAdvice<Object>

{

    private Logger logger = LoggerFactory.getLogger(ResponseAdvice.class);

    @Override

    public boolean supports(MethodParameter returnType,

            Class<? extends HttpMessageConverter<?>> converterType)

    {

        return true;

    }

    @Override

    public Object beforeBodyWrite(Object o, MethodParameter returnType,

            MediaType selectedContentType,

            Class<? extends HttpMessageConverter<?>> selectedConverterType,

            ServerHttpRequest request, ServerHttpResponse response)

    {

        logger.info("before body write param:{}",o);

        if(o instanceof String)

        {

           //序列化结果输出

           return FastJsonUtil.toJSONString(Result.success(o));

        }

        else if (o instanceof Result)

        {

             return o;

        }

        return Result.success(o);

    }

}

经过优化后,controller输出可以根据业务的需求定义输出对象。

1

2

3

4

5

6

@GetMapping("getUserByName")

   public TUser getUserByName1(@RequestParam String name)

   {

      logger.info("getUserByName paramter name:"+name);

      return userService.getUserByName(name);

   }

2.7 子系统如何实现

子系统引入common的jar包,

1

2

3

4

5

<dependency>

      <groupId>com.xx</groupId>

      <artifactId>xx-common</artifactId>

      <version>2.0</version>

  </dependency>

3、子系统定义状态码,实现BaseResultCode接口

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public enum OrderModelErrorCode implements BaseResultCode

{

    ORDER_STATUS_ERROR(1000, "订单状态不正确");

    private int code;

    private String msg;

    private UserModelErrorCode(int code, String msg)

    {

        this.code = code;

        this.msg = msg;

    }

    @Override

    public int getCode()

    {

        return code;

    }

    @Override

    public String getMsg()

    {

        return msg;

    }

}

定义异常处理类,继承公共异常处理类SysExceptionHandler

1

2

3

4

5

6

7

8

9

10

@RestControllerAdvice

public class OrderModalExceptionHandle extends SysExceptionHandler

{

     @Override

    public Result handleException(HttpServletRequest request, Exception ex)

    {

        return super.handleException(request, ex);

        //子系统可以扩展异常处理

    }

}

子系统使用示例:

1

2

3

4

5

6

7

8

9

10

11

@Override

public Order getOrder(String orderId)

{

    Order order =getOrder(orderId);

        //相关伪代码

    if(order.getStatus()>120)

    {

       throw new SysException(OrderModelErrorCode.ORDER_STATUS_ERROR);   

    }

    return order;

}

经过相关项目的重构,已经解决了第一个和第二问题,关于第三个国际化问题,将在后续的文章中讲解。

原文链接:https://juejin.cn/post/7086031796755922981
相关文章
最新更新