SpringMVC系列第14篇:实战篇:通用返回值&异常处理设计

star2017 1年前 ⋅ 370 阅读

1、本文目的

目前多数系统都采用前后端分离的方式,后端只负责提供restfull接口,返回json格式的数据就可以了,前端负责渲染。

本文带大家主要解决2个问题,在springmvc提供json格式的接口的时候,需要解决2个问题

  • 问题1:所有接口的返回值采用统一的格式
  • 问题2:系统中异常处理设计的问题,采用一种非常好的方式来解决这个问题

下面咱们一起来解决这2个问题。

2、解决问题1:实现统一的返回值

所有的接口均返回ResultDto类型的数据,ResultDto类的代码如下,主要有4个字段

  • success:表示接口是成功还是失败
  • code:错误码,当有异常的时候,可以返回具体的错误码
  • msg:提示信息,比如:操作成功、用户名有误、密码有误等等
  • data:类型是一个泛型,表示任意类型,这个用来存放接口中具体返回的数据,可以是任意类型的对象
  • 还提供了几个静态方法,方便创建ResultDto对象
  1. /**
  2. * rest接口通用返回值数据结构
  3. *
  4. * @param <T>
  5. */
  6. public class ResultDto<T> {
  7. //接口状态(成功还是失败)
  8. private Boolean success;
  9. //错误码
  10. private String code;
  11. //提示信息
  12. private String msg;
  13. //数据
  14. private T data;
  15. public Boolean getSuccess() {
  16. return success;
  17. }
  18. public void setSuccess(Boolean success) {
  19. this.success = success;
  20. }
  21. public String getCode() {
  22. return code;
  23. }
  24. public void setCode(String code) {
  25. this.code = code;
  26. }
  27. public String getMsg() {
  28. return msg;
  29. }
  30. public void setMsg(String msg) {
  31. this.msg = msg;
  32. }
  33. public T getData() {
  34. return data;
  35. }
  36. public void setData(T data) {
  37. this.data = data;
  38. }
  39. public static <T> ResultDto<T> success(T data) {
  40. return success(data, "操作成功!");
  41. }
  42. public static <T> ResultDto<T> success(T data, String msg) {
  43. ResultDto<T> result = new ResultDto<>();
  44. result.setSuccess(Boolean.TRUE);
  45. result.setMsg(msg);
  46. result.setData(data);
  47. return result;
  48. }
  49. public static <T> ResultDto<T> error(String msg) {
  50. return error(null,msg);
  51. }
  52. public static <T> ResultDto<T> error(String code,String msg) {
  53. return error(code,msg,null);
  54. }
  55. public static <T> ResultDto<T> error(String code, String msg, T data) {
  56. ResultDto<T> result = new ResultDto<>();
  57. result.setSuccess(Boolean.FALSE);
  58. result.setCode(code);
  59. result.setMsg(msg);
  60. result.setData(data);
  61. return result;
  62. }
  63. }

3、解决问题2:统一处理异常

3.1、如何做?

异常处理这块,我们的设计主要有2点,通过这2点来解决异常处理的问题

  • 第一点:定义一个基础的业务异常类(BusException),业务代码中手动抛出异常的时候,统一抛出这种类型的异常,异常类型中可以携带更详细的错误信息,比如错误码、提示信息、扩展数据等等
  • 第2点:采用springmvc全局来处理异常,控制器中不要捕获异常,将一次交给springmvc框架来统一处理。

3.2、具体代码

下面我们来看具体的代码片段,主要有2个类

业务异常类:BusException

代码比较简单,主要有2个属性和几个静态方法

  • code:异常错误码,最终会丢给ResultDto的code属性输出到客户端
  • data:异常的时候,可以传递一些扩展信息,此时可以丢到data中,最终会丢给ResultDto的data属性输出到客户端
  • 提供了几个静态方法,便于抛出BusException异常
  • 当我们的业务代码中需要抛出异常的时候,要求均抛出BusException类型的异常
  1. /**
  2. * 业务异常
  3. */
  4. public class BusException extends RuntimeException {
  5. //异常错误码
  6. private String code;
  7. //错误扩展信息
  8. private Object data;
  9. public BusException(String msg) {
  10. this(null, msg);
  11. }
  12. public BusException(String code, String msg) {
  13. this(code, msg, null);
  14. }
  15. public BusException(String code, String msg, Object data) {
  16. super(msg);
  17. this.code = code;
  18. this.data = data;
  19. }
  20. public String getCode() {
  21. return code;
  22. }
  23. public void setCode(String code) {
  24. this.code = code;
  25. }
  26. public Object getData() {
  27. return data;
  28. }
  29. public void setData(Object data) {
  30. this.data = data;
  31. }
  32. public static void throwBusException(String msg) {
  33. throwBusException(null, msg);
  34. }
  35. public static void throwBusException(String code, String msg) {
  36. throwBusException(code, msg, null);
  37. }
  38. public static void throwBusException(String code, String msg, Object data) {
  39. throw new BusException(code, msg, data);
  40. }
  41. }

全局异常统一处理类:GlobalExceptionHandle

如下代码,大家对springmvc统一异常处理不了解,建议先看一下上一篇文章。

注意下面代码中的@1,这里使用到了@RestControllerAdvice,这个注解之前没有介绍过,他和@ControllerAdvice功能类似,只是这个注解内部定义的时候上面多了一个@ResponseBody注解,表示下面这个类中处理异常的方法返回值最终都会以json格式输出到客户端

  1. /**
  2. * 全局异常处理
  3. */
  4. @RestControllerAdvice // @1
  5. public class GlobalExceptionHandle {
  6. /**
  7. * 统一处理业务异常
  8. *
  9. * @param e
  10. * @param <T>
  11. * @return
  12. */
  13. @ExceptionHandler(BusException.class)
  14. public <T> ResultDto<T> doBusException(BusException e) {
  15. //1、记录错误日志
  16. //2、返回结果
  17. return ResultDto.error(e.getCode(), e.getMessage(), (T) e.getData());
  18. }
  19. /**
  20. * 处理其他异常
  21. *
  22. * @param e
  23. * @param <T>
  24. * @return
  25. */
  26. @ExceptionHandler
  27. public <T> ResultDto<T> doException(Exception e) {
  28. //1、记录错误日志
  29. //2、返回结果
  30. return ResultDto.error("系统异常,请联系管理员,错误详情:" + e.getMessage());
  31. }
  32. }

2个问题解决了,下面我们来看看controller中如何使用。

4、Controller中代码如何写?

来个案例

如下代码,注意两点信息

  • 内部提供了2个接口,接口的返回值都是ResultDto类型的
  • 代码中,没有了try catch,而是将异常类型封装为BusException类型抛出,比如验证码有误,会抛出了BusException,顺便携带了错误码和错误提示信息,这些都会通过全局异常的处理,输出到客户端
  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4. /**
  5. * 获取用户id
  6. *
  7. * @param code
  8. * @return
  9. */
  10. @RequestMapping("/getUserName")
  11. public ResultDto<String> getUserName(@RequestParam("code") Integer code) {
  12. if (!Integer.valueOf(6666).equals(code)) {
  13. //验证码有误的时候,返回4001错误码
  14. BusException.throwBusException("4001", "验证码有误!");
  15. }
  16. return ResultDto.success("路人");
  17. }
  18. /**
  19. * 获取用户id
  20. *
  21. * @param code
  22. * @return
  23. */
  24. @RequestMapping("/getUserId")
  25. public ResultDto<String> getUserId(@RequestParam("code") Integer code) {
  26. if (!Integer.valueOf(6666).equals(code)) {
  27. BusException.throwBusException("4001", "验证码有误!");
  28. }
  29. return ResultDto.success("8888");
  30. }
  31. }

验证效果

下面我们通过idea提供的HTTP client工具搞三个测试用例,测试下接口的效果,如下图,大家可以分别运行一下3个案例。

用例1输出结果:

  1. {
  2. "success": true,
  3. "code": null,
  4. "msg": "操作成功!",
  5. "data": "路人"
  6. }

用例2输出结果:

  1. {
  2. "success": false,
  3. "code": "4001",
  4. "msg": "验证码有误!",
  5. "data": null
  6. }

用例3输出的结果:

  1. {
  2. "success": false,
  3. "code": null,
  4. "msg": "系统异常,请联系管理员,错误详情:Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: \"abc\"",
  5. "data": null
  6. }

5、总结

本文内容主要有2点:统一返回值、统一异常的处理,这2点大家要好好掌握,目前业界很少使用springmvc直接开发接口了,更多的是采用springboot来开发接口,本文的内容直接可以用到springboot中,来优化咱们的系统。

6、案例代码

  1. git地址:https://gitee.com/javacode2018/springmvc-series

最新资料

更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: