inleft
2022-02-09 9bcb19959eeb9da9bde2561e7278f6d0a55eb151
commit | author | age
9bcb19 1 /*
I 2 Copyright [2020] [https://www.xiaonuo.vip]
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8   http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15
16 Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
17
18 1.请不要删除和修改根目录下的LICENSE文件。
19 2.请不要删除和修改Snowy源码头部的版权声明。
20 3.请保留源码和相关描述文件的项目出处,作者声明等。
21 4.分发源码时候,请注明软件出处 https://gitee.com/xiaonuobase/snowy
22 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/xiaonuobase/snowy
23 6.若您的项目无法满足以上几点,可申请商业授权,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
24  */
25 package vip.xiaonuo.sys.core.error;
26
27 import cn.hutool.core.util.ObjectUtil;
28 import cn.hutool.core.util.StrUtil;
29 import cn.hutool.extra.servlet.ServletUtil;
30 import cn.hutool.log.Log;
31 import org.apache.ibatis.exceptions.PersistenceException;
32 import org.mybatis.spring.MyBatisSystemException;
33 import org.springframework.core.annotation.Order;
34 import org.springframework.http.HttpStatus;
35 import org.springframework.http.converter.HttpMessageNotReadableException;
36 import org.springframework.validation.BindException;
37 import org.springframework.validation.BindingResult;
38 import org.springframework.validation.ObjectError;
39 import org.springframework.web.HttpMediaTypeNotSupportedException;
40 import org.springframework.web.HttpRequestMethodNotSupportedException;
41 import org.springframework.web.bind.MethodArgumentNotValidException;
42 import org.springframework.web.bind.MissingServletRequestParameterException;
43 import org.springframework.web.bind.annotation.ControllerAdvice;
44 import org.springframework.web.bind.annotation.ExceptionHandler;
45 import org.springframework.web.bind.annotation.ResponseBody;
46 import org.springframework.web.bind.annotation.ResponseStatus;
47 import org.springframework.web.servlet.NoHandlerFoundException;
48 import vip.xiaonuo.core.consts.AopSortConstant;
49 import vip.xiaonuo.core.consts.CommonConstant;
50 import vip.xiaonuo.core.consts.SymbolConstant;
51 import vip.xiaonuo.core.context.requestno.RequestNoContext;
52 import vip.xiaonuo.core.exception.AuthException;
53 import vip.xiaonuo.core.exception.DemoException;
54 import vip.xiaonuo.core.exception.PermissionException;
55 import vip.xiaonuo.core.exception.ServiceException;
56 import vip.xiaonuo.core.exception.enums.*;
57 import vip.xiaonuo.core.exception.enums.abs.AbstractBaseExceptionEnum;
58 import vip.xiaonuo.core.pojo.response.ErrorResponseData;
59 import vip.xiaonuo.core.sms.modular.aliyun.exp.AliyunSmsException;
60 import vip.xiaonuo.core.sms.modular.tencent.exp.TencentSmsException;
61 import vip.xiaonuo.core.util.HttpServletUtil;
62 import vip.xiaonuo.core.util.ResponseUtil;
63
64 import javax.servlet.http.HttpServletRequest;
65 import java.util.List;
66
67 /**
68  * 全局异常处理器
69  *
70  * @author xuyuxiang
71  * @date 2020/3/18 19:03
72  */
73 @Order(AopSortConstant.GLOBAL_EXP_HANDLER_AOP)
74 @ControllerAdvice
75 public class GlobalExceptionHandler {
76
77     private static final Log log = Log.get();
78
79     /**
80      * 腾讯云短信发送异常
81      *
82      * @author yubaoshan
83      * @date 2020/6/7 18:03
84      */
85     @ExceptionHandler(TencentSmsException.class)
86     @ResponseBody
87     public ErrorResponseData aliyunSmsException(TencentSmsException e) {
88         log.error(">>> 发送短信异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), e.getErrorMessage());
89         return renderJson(500, e.getErrorMessage());
90     }
91
92     /**
93      * 阿里云短信发送异常
94      *
95      * @author yubaoshan
96      * @date 2020/6/7 18:03
97      */
98     @ExceptionHandler(AliyunSmsException.class)
99     @ResponseBody
100     public ErrorResponseData aliyunSmsException(AliyunSmsException e) {
101         log.error(">>> 发送短信异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), e.getErrorMessage());
102         return renderJson(500, e.getErrorMessage());
103     }
104
105     /**
106      * 请求参数缺失异常
107      *
108      * @author yubaoshan
109      * @date 2020/6/7 18:03
110      */
111     @ExceptionHandler(MissingServletRequestParameterException.class)
112     @ResponseBody
113     public ErrorResponseData missParamException(MissingServletRequestParameterException e) {
114         log.error(">>> 请求参数异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), e.getMessage());
115         String parameterType = e.getParameterType();
116         String parameterName = e.getParameterName();
117         String message = StrUtil.format(">>> 缺少请求的参数{},类型为{}", parameterName, parameterType);
118         return renderJson(500, message);
119     }
120
121     /**
122      * 拦截参数格式传递异常
123      *
124      * @author xuyuxiang
125      * @date 2020/4/2 15:32
126      */
127     @ExceptionHandler(HttpMessageNotReadableException.class)
128     @ResponseBody
129     public ErrorResponseData httpMessageNotReadable(HttpMessageNotReadableException e) {
130         log.error(">>> 参数格式传递异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), e.getMessage());
131         return renderJson(RequestTypeExceptionEnum.REQUEST_JSON_ERROR);
132     }
133
134     /**
135      * 拦截不支持媒体类型异常
136      *
137      * @author xuyuxiang
138      * @date 2020/4/2 15:38
139      */
140     @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
141     @ResponseBody
142     public ErrorResponseData httpMediaTypeNotSupport(HttpMediaTypeNotSupportedException e) {
143         log.error(">>> 参数格式传递异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), e.getMessage());
144         return renderJson(RequestTypeExceptionEnum.REQUEST_TYPE_IS_JSON);
145     }
146
147     /**
148      * 拦截请求方法的异常
149      *
150      * @author xuyuxiang
151      * @date 2020/3/18 19:14
152      */
153     @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
154     @ResponseBody
155     public ErrorResponseData methodNotSupport(HttpServletRequest request) {
156         if (ServletUtil.isPostMethod(request)) {
157             log.error(">>> 请求方法异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), RequestMethodExceptionEnum.REQUEST_METHOD_IS_GET.getMessage());
158             return renderJson(RequestMethodExceptionEnum.REQUEST_METHOD_IS_GET);
159         }
160         if (ServletUtil.isGetMethod(request)) {
161             log.error(">>> 请求方法异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), RequestMethodExceptionEnum.REQUEST_METHOD_IS_POST.getMessage());
162             return renderJson(RequestMethodExceptionEnum.REQUEST_METHOD_IS_POST);
163         }
164         return null;
165     }
166
167     /**
168      * 拦截资源找不到的运行时异常
169      *
170      * @author xuyuxiang
171      * @date 2020/4/2 15:38
172      */
173     @ExceptionHandler(NoHandlerFoundException.class)
174     @ResponseStatus(HttpStatus.NOT_FOUND)
175     @ResponseBody
176     public ErrorResponseData notFound(NoHandlerFoundException e) {
177         log.error(">>> 资源不存在异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), e.getMessage() +",请求地址为:" + HttpServletUtil.getRequest().getRequestURI());
178         return renderJson(PermissionExceptionEnum.URL_NOT_EXIST.getCode(), PermissionExceptionEnum.URL_NOT_EXIST.getMessage() +",请求地址为:" + HttpServletUtil.getRequest().getRequestURI());
179     }
180
181     /**
182      * 拦截参数校验错误异常,JSON传参
183      *
184      * @author xuyuxiang
185      * @date 2020/4/2 15:38
186      */
187     @ExceptionHandler(MethodArgumentNotValidException.class)
188     @ResponseBody
189     public ErrorResponseData methodArgumentNotValidException(MethodArgumentNotValidException e) {
190         String argNotValidMessage = getArgNotValidMessage(e.getBindingResult());
191         log.error(">>> 参数校验错误异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), argNotValidMessage);
192         return renderJson(ParamExceptionEnum.PARAM_ERROR.getCode(), argNotValidMessage);
193     }
194
195     /**
196      * 拦截参数校验错误异常
197      *
198      * @author xuyuxiang
199      * @date 2020/3/18 19:41
200      */
201     @ExceptionHandler(BindException.class)
202     @ResponseBody
203     public ErrorResponseData paramError(BindException e) {
204         String argNotValidMessage = getArgNotValidMessage(e.getBindingResult());
205         log.error(">>> 参数校验错误异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), argNotValidMessage);
206         return renderJson(ParamExceptionEnum.PARAM_ERROR.getCode(), argNotValidMessage);
207     }
208
209     /**
210      * 拦截认证失败异常
211      *
212      * @author xuyuxiang
213      * @date 2020/3/18 19:41
214      */
215     @ExceptionHandler(AuthException.class)
216     @ResponseBody
217     public ErrorResponseData authFail(AuthException e) {
218         log.error(">>> 认证异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), e.getMessage());
219         return renderJson(e.getCode(), e.getErrorMessage());
220     }
221
222     /**
223      * 拦截权限异常
224      *
225      * @author xuyuxiang
226      * @date 2020/3/18 19:41
227      */
228     @ExceptionHandler(PermissionException.class)
229     @ResponseBody
230     public ErrorResponseData noPermission(PermissionException e) {
231         log.error(">>> 权限异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), e.getMessage() +",请求地址为:" + HttpServletUtil.getRequest().getRequestURI());
232         return renderJson(e.getCode(), e.getErrorMessage() + ",请求地址为:" + HttpServletUtil.getRequest().getRequestURI());
233     }
234
235     /**
236      * 拦截业务异常
237      *
238      * @author xuyuxiang
239      * @date 2020/3/18 19:41
240      */
241     @ExceptionHandler(ServiceException.class)
242     @ResponseBody
243     public ErrorResponseData businessError(ServiceException e) {
244         log.error(">>> 业务异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), e.getMessage());
245         return renderJson(e.getCode(), e.getErrorMessage(), e);
246     }
247
248     /**
249      * 拦截mybatis数据库操作的异常
250      * <p>
251      * 用在demo模式,拦截DemoException
252      *
253      * @author yubaoshan
254      * @date 2020/5/5 15:19
255      */
256     @ExceptionHandler(MyBatisSystemException.class)
257     @ResponseBody
258     public ErrorResponseData persistenceException(MyBatisSystemException e) {
259         log.error(">>> mybatis操作出现异常,请求号为:{},具体信息为:{}", RequestNoContext.get(), e.getMessage());
260         Throwable cause = e.getCause();
261         if (cause instanceof PersistenceException) {
262             Throwable secondCause = cause.getCause();
263             if (secondCause instanceof DemoException) {
264                 DemoException demoException = (DemoException) secondCause;
265                 return ResponseUtil.responseDataError(demoException.getCode(), demoException.getErrorMessage(), e.getStackTrace()[0].toString());
266             }
267         }
268         return ResponseUtil.responseDataError(ServerExceptionEnum.SERVER_ERROR.getCode(), ServerExceptionEnum.SERVER_ERROR.getMessage(), e.getStackTrace()[0].toString());
269     }
270
271     /**
272      * 拦截未知的运行时异常
273      *
274      * @author xuyuxiang
275      * @date 2020/3/18 19:41
276      */
277     @ExceptionHandler(Throwable.class)
278     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
279     @ResponseBody
280     public ErrorResponseData serverError(Throwable e) {
281         log.error(">>> 服务器运行异常,请求号为:{}", RequestNoContext.get());
282         e.printStackTrace();
283         return renderJson(e);
284     }
285
286     /**
287      * 渲染异常json
288      *
289      * @author yubaoshan
290      * @date 2020/5/5 16:22
291      */
292     private ErrorResponseData renderJson(Integer code, String message) {
293         return renderJson(code, message, null);
294     }
295
296     /**
297      * 渲染异常json
298      *
299      * @author yubaoshan
300      * @date 2020/5/5 16:22
301      */
302     private ErrorResponseData renderJson(AbstractBaseExceptionEnum baseExceptionEnum) {
303         return renderJson(baseExceptionEnum.getCode(), baseExceptionEnum.getMessage(), null);
304     }
305
306     /**
307      * 渲染异常json
308      *
309      * @author yubaoshan
310      * @date 2020/5/5 16:22
311      */
312     private ErrorResponseData renderJson(Throwable throwable) {
313         return renderJson(((AbstractBaseExceptionEnum) ServerExceptionEnum.SERVER_ERROR).getCode(),
314                 ((AbstractBaseExceptionEnum) ServerExceptionEnum.SERVER_ERROR).getMessage(), throwable);
315     }
316
317     /**
318      * 渲染异常json
319      * <p>
320      * 根据异常枚举和Throwable异常响应,异常信息响应堆栈第一行
321      *
322      * @author yubaoshan
323      * @date 2020/5/5 16:22
324      */
325     private ErrorResponseData renderJson(Integer code, String message, Throwable e) {
326         if (ObjectUtil.isNotNull(e)) {
327
328             //获取所有堆栈信息
329             StackTraceElement[] stackTraceElements = e.getStackTrace();
330
331             //默认的异常类全路径为第一条异常堆栈信息的
332             String exceptionClassTotalName = stackTraceElements[0].toString();
333
334             //遍历所有堆栈信息,找到vip.xiaonuo开头的第一条异常信息
335             for (StackTraceElement stackTraceElement : stackTraceElements) {
336                 if (stackTraceElement.toString().contains(CommonConstant.DEFAULT_PACKAGE_NAME)) {
337                     exceptionClassTotalName = stackTraceElement.toString();
338                     break;
339                 }
340             }
341             return ResponseUtil.responseDataError(code, message, exceptionClassTotalName);
342         } else {
343             return ResponseUtil.responseDataError(code, message, null);
344         }
345     }
346
347     /**
348      * 获取请求参数不正确的提示信息
349      * <p>
350      * 多个信息,拼接成用逗号分隔的形式
351      *
352      * @author yubaoshan
353      * @date 2020/5/5 16:50
354      */
355     private String getArgNotValidMessage(BindingResult bindingResult) {
356         if (bindingResult == null) {
357             return "";
358         }
359         StringBuilder stringBuilder = new StringBuilder();
360
361         //多个错误用逗号分隔
362         List<ObjectError> allErrorInfos = bindingResult.getAllErrors();
363         for (ObjectError error : allErrorInfos) {
364             stringBuilder.append(SymbolConstant.COMMA).append(error.getDefaultMessage());
365         }
366
367         //最终把首部的逗号去掉
368         return StrUtil.removePrefix(stringBuilder.toString(), SymbolConstant.COMMA);
369     }
370
371 }