一、通用异常返回
通用异常返回一般用在该异常服务器无法处理的时候,进行消息的返回。所以返回代码只有 500。
/**
* 通用异常
*/
@Getter
@Setter
public class CommonException extends RuntimeException {
private final Integer code;
private final String msg;
public CommonException() {
super("服务器异常");
this.code = 500;
this.msg = "服务器异常";
}
public CommonException(String msg, Object... arguments) {
super(CharSequenceUtil.format(msg, arguments));
this.code = 500;
this.msg = CharSequenceUtil.format(msg, arguments);
}
public CommonException(Integer code, String msg, Object... arguments) {
super(CharSequenceUtil.format(msg, arguments));
this.code = code;
this.msg = CharSequenceUtil.format(msg, arguments);
}
}
二、未知错误返回格式重写
将未知错误异常,输出格式重写为我们熟悉的响应格式。
/**
* 将未知错误异常,输出格式重写为我们熟悉的响应格式
**/
@Component
public class GlobalErrorAttributesHandler extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions attributeOptions) {
final String T = "服务器异常,请求地址:";
// 获取spring默认的返回内容
Map<String, Object> defaultErrorAttributes = super.getErrorAttributes(webRequest, attributeOptions);
// 获取其状态码
Object status = defaultErrorAttributes.get("status");
if (ObjectUtil.isNotEmpty(status)) {
// 如果其为404,则处理
if (HttpStatus.HTTP_NOT_FOUND == Convert.toInt(status)) {
Object path = defaultErrorAttributes.get("path");
if(ObjectUtil.isNotEmpty(path)) {
return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_NOT_FOUND, "路径不存在,请求地址:" +
Convert.toStr(path), null));
} else {
return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_NOT_FOUND, "路径不存在", null));
}
} else {
return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_INTERNAL_ERROR, T +
CommonServletUtil.getRequest().getRequestURL(), null));
}
}
// 如果返回的异常是CommonException,则按CommonException响应的内容进行返回
Throwable throwable = this.getError(webRequest);
if (ObjectUtil.isNotEmpty(throwable)) {
if (throwable instanceof CommonException) {
CommonException commonException = (CommonException) throwable;
return BeanUtil.beanToMap(CommonResult.error(commonException.getMsg()));
} else {
return BeanUtil.beanToMap(CommonResult.get(HttpStatus.HTTP_INTERNAL_ERROR, T +
CommonServletUtil.getRequest().getRequestURL(), null));
}
} else {
// throwable为空,则直接返回默认异常
return BeanUtil.beanToMap(CommonResult.error(T + CommonServletUtil.getRequest().getRequestURL()));
}
}
}
三、各类异常处理
3.1 全局异常页面处理
全局异常页面处理器,覆盖默认的 Whitelabel Error Page
/**
* 全局异常页面处理器,覆盖默认的 Whitelabel Error Page
*
**/
@Slf4j
@RestController
public class GlobalErrorViewController {
/**
* Error页面视图,直接响应JSON
**/
@RequestMapping("/errorView")
public CommonResult<String> globalError(HttpServletRequest request) {
CommonResult<String> commonResult = new CommonResult<>(404, "路径不存在", null);
Object model = request.getAttribute("model");
if(ObjectUtil.isNotEmpty(model) && (model instanceof Exception)){
if(model instanceof CommonException) {
JSONObject errorObj = JSONUtil.parseObj(model);
Integer code = errorObj.getInt("code");
String msg = errorObj.getStr("msg");
if(ObjectUtil.isAllNotEmpty(code, msg)) {
commonResult.setCode(code).setMsg(msg);
} else if(ObjectUtil.isNotEmpty(msg)) {
commonResult = CommonResult.error(msg);
} else {
commonResult = CommonResult.error();
}
} else {
commonResult = CommonResult.error();
log.error(">>> 服务器未知异常,具体信息:", (Exception) model);
}
}
return commonResult;
}
}
@RestController
public class GlobalErrorViewHandler extends BasicErrorController {
public GlobalErrorViewHandler(ServerProperties serverProperties) {
super(new GlobalErrorAttributesHandler(), serverProperties.getError());
}
/**
* 覆盖默认的Json响应
**/
@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> defaultErrorAttributes = super.getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
Integer code = Convert.toInt(defaultErrorAttributes.get("code"));
return new ResponseEntity<>(defaultErrorAttributes, HttpStatus.valueOf(ObjectUtil.isNotEmpty(code)?code:500));
}
/**
* 覆盖默认的错误页面,响应JSON
*/
@Override
@RequestMapping(produces = {
"text/html"})
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = this.getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
request.setAttribute("model", model);
return modelAndView != null ? modelAndView : new ModelAndView("errorView", model);
}
}
3.2 全局异常处理器
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 不同异常返回不同结果
**/
@ResponseBody
@ExceptionHandler
public CommonResult<String> handleException(Exception e) {
return GlobalExceptionUtil.getCommonResult(e);
}
}
3.3 全局异常处理工具类
这里我们将可能遇到的异常和对应的处理器写入,像这样:
/**
* 全局异常处理工具类,将异常转为通用结果
*/
@Slf4j
public class GlobalExceptionUtil {
/**
* 实用程序类是静态成员的集合,并不意味着可以实例化。 即使是可以扩展的抽象实用程序类也不应该具有公共构造函数。
* Java 向每个未显式定义至少一个的类添加一个隐式公共构造函数。 因此,至少应该定义一个非公共构造函数。
*/
private GlobalExceptionUtil(){
// TODO
}
/**
* 根据错误类型获取对应的CommonResult
**/
public static CommonResult<String> getCommonResult(Exception e) {
CommonResult<String> commonResult;
if (e instanceof HttpMessageNotReadableException) {
// 如果是参数传递格式不支持异常 415
if (Objects.requireNonNull(e.getMessage()).contains("JSON parse error")) {
e.printStackTrace();
//JSON格式转换错误特殊提示
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "参数格式错误", null);
} else {
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "请使用JSON方式传参", null);
}
}
else if (e instanceof HttpMediaTypeNotSupportedException) {
e.printStackTrace();
// 如果是JSON参数格式错误异常 415
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "参数格式错误", null);
}
else if (e instanceof MethodArgumentNotValidException) {
// 如果是参数校验异常(MethodArgumentNotValidException) 415
MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException) e;
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, getArgNotValidMessage(methodArgumentNotValidException.getBindingResult()), null);
}
else if (e instanceof BindException) {
// 如果是参数校验异常(BindException) 415
BindException bindException = (BindException) e;
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, getArgNotValidMessage(bindException.getBindingResult()), null);
}
else if (e instanceof ConstraintViolationException) {
// 如果是参数校验异常(ConstraintViolationException) 415
ConstraintViolationException constraintViolationException = (ConstraintViolationException) e;
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, getArgNotValidMessage(constraintViolationException.getConstraintViolations()), null);
}
else if (e instanceof MissingServletRequestParameterException) {
// 如果是参数校验异常(MissingServletRequestParameterException) 415
MissingServletRequestParameterException missingServletRequestParameterException = (MissingServletRequestParameterException) e;
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, missingServletRequestParameterException.getMessage(), null);
}
else if (e instanceof SaTokenException) {
// 如果是SaToken相关异常,则由AuthExceptionUtil处理
return AuthExceptionUtil.getCommonResult(e);
}
else if (e instanceof MultipartException) {
//文件上传错误特殊提示
commonResult = CommonResult.error("请使用multipart/form-data方式上传文件");
}
else if (e instanceof MissingServletRequestPartException) {
//文件上传错误特殊提示
commonResult = CommonResult.error("请选择要上传的文件并检查文件参数名称是否正确");
}
else if(e instanceof MyBatisSystemException) {
// 如果是MyBatisSystemException
Throwable cause = e.getCause();
if (cause instanceof PersistenceException) {
Throwable secondCause = cause.getCause();
if (secondCause instanceof CommonException) {
CommonException commonException = (CommonException) secondCause;
commonResult = CommonResult.get(commonException.getCode(), commonException.getMsg(), null);
} else {
e.printStackTrace();
commonResult = CommonResult.error("数据操作异常");
}
} else {
e.printStackTrace();
commonResult = CommonResult.error("数据操作异常");
}
}
else if (e instanceof CommonException) {
// 通用业务异常,直接返回给前端
CommonException commonException = (CommonException) e;
commonResult = CommonResult.get(commonException.getCode(), commonException.getMsg(), null);
}
else if (e instanceof HttpRequestMethodNotSupportedException){
// 如果是请求方法异常 405
String method = CommonServletUtil.getRequest().getMethod();
if (HttpMethod.GET.toString().equals(method)) {
commonResult = CommonResult.get(HttpStatus.HTTP_BAD_METHOD, "请求方法应为POST", null);
} else if(HttpMethod.POST.toString().equals(method)) {
commonResult = CommonResult.get(HttpStatus.HTTP_BAD_METHOD, "请求方法应为GET", null);
} else {
commonResult = CommonResult.get(HttpStatus.HTTP_BAD_METHOD, "请求方法仅支持GET或POST", null);
}
}
else {
// 未知异常打印详情
e.printStackTrace();
// 未知异常返回服务器异常
commonResult = CommonResult.error("服务器异常");
}
log.error(">>> {},请求地址:{}", commonResult.getMsg(), CommonServletUtil.getRequest().getRequestURL());
return commonResult;
}
/**
* 获取请求参数不正确的提示信息,多个信息,拼接成用逗号分隔的形式
**/
public static String getArgNotValidMessage(Set<ConstraintViolation<?>> constraintViolationSet) {
if (ObjectUtil.isEmpty(constraintViolationSet)) {
return "";
}
StringBuilder stringBuilder = StrUtil.builder();
// 多个错误用逗号分隔
for (ConstraintViolation<?> constraintViolation : constraintViolationSet) {
stringBuilder.append(StrPool.COMMA).append(constraintViolation.getMessage());
}
// 最终把首部的逗号去掉
return CharSequenceUtil.removePrefix(stringBuilder.toString(), StrPool.COMMA);
}
/**
* 获取请求参数不正确的提示信息,多个信息,拼接成用逗号分隔的形式
**/
public static String getArgNotValidMessage(BindingResult bindingResult) {
if (ObjectUtil.isNull(bindingResult)) {
return "";
}
StringBuilder stringBuilder = StrUtil.builder();
// 多个错误用逗号分隔
List<ObjectError> allErrorInfos = bindingResult.getAllErrors();
for (ObjectError error : allErrorInfos) {
stringBuilder.append(StrPool.COMMA).append(error.getDefaultMessage());
}
// 最终把首部的逗号去掉
return CharSequenceUtil.removePrefix(stringBuilder.toString(), StrPool.COMMA);
}
}