Spring Boot异常统一处理

左手的ㄟ右手 2022-05-24 09:20 302阅读 0赞

最近在学习自己搭建一个配置中心平台,准备用spring boot来搭建后台web系统,将遇到的问题在此记录。github项目地址:点击打开链接。

我们在用ajax向服务端请求数据时,免不了会有异常。如果不进行统一处理,直接把异常信息抛到前端,界面会很不友好。spring boot可以通过使用@ControllerAdvice来进行统一异常处理,@ExceptionHandler(value = Exception.class)来指定捕获的异常。

1.自定义异常类

首先,我们自定义一个异常类。在后台业务处理时,如果是可知的业务异常,我们直接抛出此类异常。当捕获到此类异常时,可以直接把异常信息返回到前端。代码如下:

  1. package com.kevin.confcenter.common.exception;
  2. /**
  3. * @Author: kevin
  4. * @Description: 基础异常类
  5. * @Date: Created In 2018/3/10 14:51
  6. */
  7. public abstract class ConfCenterException extends RuntimeException {
  8. /**
  9. * uid
  10. */
  11. private static final long serialVersionUID = 8037891447646609768L;
  12. /**
  13. * 默认构造函数
  14. */
  15. public ConfCenterException() {
  16. }
  17. /**
  18. * 构造函数
  19. * @param errMsg 异常消息
  20. */
  21. public ConfCenterException(String errMsg) {
  22. super(errMsg);
  23. }
  24. /**
  25. * 构造函数
  26. * @param cause 原始异常
  27. */
  28. public ConfCenterException(Throwable cause) {
  29. super(cause);
  30. }
  31. /**
  32. * 构造函数
  33. * @param errMsg 异常消息
  34. * @param cause 原始异常
  35. */
  36. public ConfCenterException(String errMsg, Throwable cause) {
  37. super(errMsg, cause);
  38. }
  39. }

2.自定义返回数据类

我们自定义一个返回结果类,包装返回前端数据信息,包括状态、错误信息、数据等,所有的ajax请求,数据都用此类包装后返回前端。我们统一处理异常时,也会返回这个类对象。这样,前端根据状态码,就可以做出相应的操作,抛出封装好的错误信息或者跳转到指定的页面。代码如下

  1. package com.kevin.confcenter.common.bean.vo;
  2. /**
  3. * 客户端的HTTP调用的应答结果类
  4. */
  5. public class ResultInfo {
  6. /**
  7. * 应答结果状态码——成功
  8. */
  9. public static final int RESULT_CODE_SUCCESS = 0;
  10. /**
  11. * 应答结果状态码——通用错误
  12. */
  13. public static final int RESULT_CODE_COMMONERR = 9999;
  14. /**
  15. * session过期
  16. */
  17. public static final int RESULT_SESSION_TIMEOUT = 1;
  18. /**
  19. * 返回状态
  20. */
  21. private int status = RESULT_CODE_SUCCESS;
  22. /**
  23. * 返回状态描述
  24. */
  25. private String statusInfo = "SUCCESS"; // 操作结果描述信息
  26. /**
  27. * 返回数据
  28. */
  29. private Object data;// 操作返回数据绑定
  30. /**
  31. * 返回一个默认的错误结果
  32. *
  33. * @return 错误结果
  34. */
  35. public static ResultInfo error() {
  36. ResultInfo res = new ResultInfo(RESULT_CODE_COMMONERR, "ERROR");
  37. return res;
  38. }
  39. /**
  40. * 返回一个带错误信息的错误结果
  41. *
  42. * @param errorMessage 错误信息
  43. * @return 错误结果
  44. */
  45. public static ResultInfo errorMessage(String errorMessage) {
  46. ResultInfo res = new ResultInfo(RESULT_CODE_COMMONERR, errorMessage);
  47. return res;
  48. }
  49. /**
  50. * session过期
  51. *
  52. * @return
  53. */
  54. public static ResultInfo sessionTimeout() {
  55. ResultInfo res = new ResultInfo(RESULT_SESSION_TIMEOUT, "登录超时");
  56. return res;
  57. }
  58. /**
  59. * 返回一个带错误信息和数据的错误结果
  60. *
  61. * @param errorMessage 错误信息
  62. * @param data 数据
  63. * @return 错误结果
  64. */
  65. public static ResultInfo errorMessage(String errorMessage, Object data) {
  66. ResultInfo res = new ResultInfo(RESULT_CODE_COMMONERR, errorMessage);
  67. res.setData(data);
  68. return res;
  69. }
  70. /**
  71. * 返回一个带状态和信息的结果
  72. *
  73. * @param status 状态
  74. * @param info 信息
  75. * @return 返回结果
  76. */
  77. public static ResultInfo result(int status, String info) {
  78. ResultInfo res = new ResultInfo();
  79. res.status = status;
  80. res.statusInfo = info;
  81. return res;
  82. }
  83. /**
  84. * 返回一个带状态,信息和数据的结果
  85. *
  86. * @param status 状态
  87. * @param info 信息
  88. * @param data 数据
  89. * @return 返回结果
  90. */
  91. public static ResultInfo result(int status, String info, Object data) {
  92. ResultInfo res = new ResultInfo();
  93. res.status = status;
  94. res.statusInfo = info;
  95. res.data = data;
  96. return res;
  97. }
  98. /**
  99. * 返回一个成功结果
  100. *
  101. * @return 成功结果
  102. */
  103. public static ResultInfo success() {
  104. ResultInfo res = new ResultInfo();
  105. return res;
  106. }
  107. /**
  108. * 返回一个带数据的成功结果
  109. *
  110. * @param data 数据
  111. * @return 成功结果
  112. */
  113. public static ResultInfo success(Object data) {
  114. ResultInfo res = new ResultInfo();
  115. res.setData(data);
  116. return res;
  117. }
  118. /**
  119. * 返回一个带信息的成功结果
  120. *
  121. * @param message 提示信息
  122. * @return 成功结果
  123. */
  124. public static ResultInfo successMessage(String message) {
  125. ResultInfo res = new ResultInfo(RESULT_CODE_SUCCESS, message);
  126. return res;
  127. }
  128. /**
  129. * 默认构造函数
  130. */
  131. public ResultInfo() {
  132. }
  133. /**
  134. * 带状态和信息的构造函数
  135. *
  136. * @param status 状态
  137. * @param statusInfo 提示信息
  138. */
  139. public ResultInfo(int status, String statusInfo) {
  140. this.status = status;
  141. this.statusInfo = statusInfo;
  142. }
  143. /**
  144. * 带状态,信息和数据的构造函数
  145. *
  146. * @param status 状态
  147. * @param statusInfo 提示信息
  148. * @param data 数据
  149. */
  150. public ResultInfo(int status, String statusInfo, Object data) {
  151. super();
  152. this.status = status;
  153. this.statusInfo = statusInfo;
  154. this.data = data;
  155. }
  156. public Object getData() {
  157. return data;
  158. }
  159. public int getStatus() {
  160. return status;
  161. }
  162. public String getStatusInfo() {
  163. return statusInfo;
  164. }
  165. public void setData(Object data) {
  166. this.data = data;
  167. }
  168. public void setStatus(int status) {
  169. this.status = status;
  170. }
  171. public void setStatusInfo(String statusInfo) {
  172. this.statusInfo = statusInfo;
  173. }
  174. @Override
  175. public int hashCode() {
  176. final int prime = 31;
  177. int result = 1;
  178. result = prime * result + ((data == null) ? 0 : data.hashCode());
  179. result = prime * result + status;
  180. result = prime * result + ((statusInfo == null) ? 0 : statusInfo.hashCode());
  181. return result;
  182. }
  183. @Override
  184. public boolean equals(Object obj) {
  185. if (this == obj) {
  186. return true;
  187. }
  188. if (obj == null) {
  189. return false;
  190. }
  191. if (getClass() != obj.getClass()) {
  192. return false;
  193. }
  194. ResultInfo other = (ResultInfo) obj;
  195. if (data == null) {
  196. if (other.data != null) {
  197. return false;
  198. }
  199. } else if (!data.equals(other.data)) {
  200. return false;
  201. }
  202. if (status != other.status) {
  203. return false;
  204. }
  205. if (statusInfo == null) {
  206. if (other.statusInfo != null) {
  207. return false;
  208. }
  209. } else if (!statusInfo.equals(other.statusInfo)) {
  210. return false;
  211. }
  212. return true;
  213. }
  214. }

3.异常统一处理

使用@ControllerAdvice来进行统一异常处理,@ExceptionHandler(value = Exception.class)来指定捕获的异常。代码如下:

  1. package com.kevin.confcenter.admin.extend;
  2. import com.kevin.confcenter.common.bean.vo.ResultInfo;
  3. import com.kevin.confcenter.common.exception.ConfCenterException;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.web.bind.annotation.ControllerAdvice;
  7. import org.springframework.web.bind.annotation.ResponseBody;
  8. /**
  9. * @Author: kevin
  10. * @Description: 异常统一处理
  11. * @Date: Created In 2018/4/16 10:25
  12. */
  13. @ControllerAdvice
  14. public class ExceptionHandler {
  15. private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandler.class);
  16. @org.springframework.web.bind.annotation.ExceptionHandler(value = Exception.class)
  17. @ResponseBody
  18. public ResultInfo handler(Exception e) {
  19. if (e instanceof ConfCenterException) {
  20. return ResultInfo.errorMessage(e.getMessage());
  21. } else {
  22. LOGGER.error("exception:{}", e.getMessage(), e);
  23. return ResultInfo.errorMessage("服务器内部错误");
  24. }
  25. }
  26. }

4.前端ajax封装

前端封装ajax方法,可以实现防重复提交和针对错误码进行相应的处理。代码如下:

  1. // 系统全局的ajax队列
  2. ajax_queue: [],
  3. ajax: function (options) {
  4. if (options.lu_ajax_id) {
  5. // 检查之前的req是否已经完成
  6. if (conf.utils.ajax_queue.contains(options.lu_ajax_id)) {
  7. $.fn.alert('不要频繁重复操作,请稍后再试.');
  8. return;
  9. }
  10. // complete回调,用于移除之前的ajax queue中的请求标记
  11. options.complete = function (jqXHR, textStatus) {
  12. var index = conf.utils.ajax_queue.indexOf(options.lu_ajax_id);
  13. if (index > -1) {
  14. conf.utils.ajax_queue.splice(index, 1);
  15. }
  16. }
  17. conf.utils.ajax_queue.push(options.lu_ajax_id);
  18. }
  19. if (!options.dataType) {
  20. options.dataType = "json";
  21. }
  22. if (!options.timeout) {
  23. options.timeout = 1000 * 60 * 60;
  24. }
  25. if (!options.timeout) {
  26. options.timeout = 1000 * 60 * 3;
  27. }
  28. if (options.url.indexOf('?') > -1) {//加入时间戳
  29. options.url += '&' + new Date().getTime();
  30. } else {
  31. options.url += '?' + new Date().getTime();
  32. }
  33. if (!options.success) { //没有加入自定义的success回调函数,则调用默认回调函数
  34. options.success = function (res, textStatus, jqXHR) {
  35. if (res.status != 0) { //如果返回结果消息状态码非零则表示失败,弹出错误信息
  36. if (res.status == 1) {
  37. window.location.href = "/user/login";
  38. } else {
  39. if (options.fail) {
  40. options.fail.call(this, res, textStatus, jqXHR);
  41. return;
  42. }
  43. $.fn.alert(res.statusInfo);
  44. return;
  45. }
  46. }
  47. if (options.ok) { // 如果有自定义ok回调,则在结果码为成功时回调
  48. options.ok.call(this, res, textStatus, jqXHR);
  49. }
  50. }
  51. }
  52. if (!options.error) { // 没有加入自定义的error回调函数,则指定默认回调
  53. options.error = function (res, textStatus, jqXHR) {
  54. $.fn.alert("ERROR:" + jqXHR);
  55. }
  56. }
  57. return $.ajax(options);
  58. }

5.实例

我们以登录接口为例,先在controller加一个登录方法,不做任何处理,直接抛出BusinessException异常,BusinessException是继承自ConfCenterException类,代码如下:

  1. /**
  2. * 登录
  3. *
  4. * @param
  5. * @return
  6. */
  7. @RequestMapping(value = "/login", method = RequestMethod.POST)
  8. @ResponseBody
  9. public ResultInfo login(HttpServletRequest request, String userName, String password) {
  10. throw new BusinessException("test");
  11. }

前端ajax请求如下:

  1. conf.utils.ajax({
  2. url: '/user/login',
  3. type: 'POST',
  4. async: false,
  5. data: data,
  6. ok: function (res, textStatus, jqXHR) {
  7. if (res.status == 0) {
  8. window.location.href = "/index";
  9. }
  10. }
  11. });

当我们在前端点击登录时,就会弹窗提示,直接显示我们的异常信息,效果如下:

70

发表评论

表情:
评论列表 (有 0 条评论,302人围观)

还没有评论,来说两句吧...

相关阅读