功能
springboot请求全异常处理,已在公司项目使用,处理所有请求异常,自定义异常,未捕获异常的统一格式输出处理
可配合@Validated
进行请求参数校验,让代码更优雅、健壮
pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
请求全异常handler处理类
package com.hongkun.exception.handler;
import com.hongkun.tools.ResponseUtil;
import com.hongkun.enums.ResultEnum;
import com.hongkun.exception.GlobalException;
import com.hongkun.tools.LoggerUtil;
import com.hongkun.model.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.*;
@ResponseBody
@ControllerAdvice
@Slf4j
public class ExceptionHandlers {
@ExceptionHandler(GlobalException.class)
public ResultVO handler(HttpServletRequest request, GlobalException e) {
log.error("【全局自定义异常】: err = {} url = {} ", LoggerUtil.getLogReplace(e.getMessage()), request.getRequestURL());
return ResponseUtil.error(e.getCode(), e.getMessage());
}
/**
* 忽略参数异常处理器
*
* @param e 忽略参数异常
* @return ResponseResult
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
public ResultVO parameterMissingExceptionHandler(HttpServletRequest request, MissingServletRequestParameterException e) {
log.error("【忽略参数异常】: err = {} url = {} ", LoggerUtil.getLogReplace(e.getMessage()), request.getRequestURL());
return ResponseUtil.error(ResultEnum.PARAM_ERROR.getCode(), "请求参数 " + e.getParameterName() + " 不能为空");
}
/**
* 缺少请求体异常处理器
*
* @param e 缺少请求体异常
* @return ResponseResult
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResultVO parameterBodyMissingExceptionHandler(HttpServletRequest request, HttpMessageNotReadableException e) {
log.error("【缺少请求体异常】: err = {} url = {} ", LoggerUtil.getLogReplace(e.getMessage()), request.getRequestURL());
return ResponseUtil.error(ResultEnum.PARAM_ERROR.getCode(), "参数体不能为空");
}
/**
* 参数效验异常处理器
*
* @param ex
* @return
*/
@ResponseBody
@ExceptionHandler(ConstraintViolationException.class)
public ResultVO constraintViolationExceptionHandler(HttpServletRequest request, ConstraintViolationException ex) {
log.error("【参数效验异常】: err = {} url = {} ", LoggerUtil.getLogReplace(ex.getMessage()), request.getRequestURL());
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
List<String> msgList = new ArrayList<>();
while (iterator.hasNext()) {
ConstraintViolation<?> cvl = iterator.next();
msgList.add(cvl.getMessageTemplate());
}
if (!msgList.isEmpty()) {
return ResponseUtil.error(ResultEnum.PARAM_ERROR.getCode(), msgList.get(0));
}
return ResponseUtil.error(ResultEnum.PARAM_ERROR);
}
/**
* 参数效验异常处理器
*
* @param e 参数验证异常
* @return ResponseInfo
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResultVO parameterExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException e) {
// 获取异常信息
BindingResult exceptions = e.getBindingResult();
// 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息
if (exceptions.hasErrors()) {
List<ObjectError> errors = exceptions.getAllErrors();
if (!errors.isEmpty()) {
// 这里列出了全部错误参数,按正常逻辑,只需要第一条错误即可
FieldError fieldError = (FieldError) errors.get(0);
log.error("【参数验证异常】: err = {} url = {} ", fieldError.getDefaultMessage(), request.getRequestURL());
return ResponseUtil.error(ResultEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
}
}
return ResponseUtil.error(ResultEnum.PARAM_ERROR);
}
/**
* 不支持当前媒体类型 参数类型不对
*
* @param e
* @return
*/
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public ResultVO handleHttpMediaTypeNotSupportedException(HttpServletRequest request, HttpMediaTypeNotSupportedException e) {
log.error("【请求异常】: err = {} url = {} ", e.getMessage(), request.getRequestURL());
return ResponseUtil.error(ResultEnum.PARAM_ERROR.getCode(), e.getMessage());
}
/**
* 上传文件过大异常
*
* @param e
* @return
*/
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResultVO handleHttpMediaTypeNotSupportedException(HttpServletRequest request, MaxUploadSizeExceededException e) {
log.error("【请求异常】: err = {} url = {} ", e.getMessage(), request.getRequestURL());
return ResponseUtil.error(ResultEnum.FILE_PARAM_ERROR);
}
/**
* 未捕获异常处理器
*
* @param e 未捕获异常
* @return ResponseInfo
*/
@ExceptionHandler(Exception.class)
public ResultVO unCatchExceptionHandler(HttpServletRequest request, Exception e) {
e.printStackTrace();
log.error("【未捕获异常】: err = {} url = {}", LoggerUtil.getLogReplace(e.getMessage()), request.getRequestURL());
return ResponseUtil.error(ResultEnum.SERVER_ERROR);
}
}
统一格式封装vo类
package com.chai.model.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author chaixuhong
* @apiNote 数据返回标准化输出类
* @date 2019-08-12
*/
@ApiModel(value = "返回类")
@Data
public class ResultVO<T> implements Serializable {
private static final long serialVersionUID = 3068837394742385883L;
@ApiModelProperty(value = "错误码,默认1是成功,0是失败;以及其它错误码",example = "1")
private Integer code;
@ApiModelProperty("提示信息")
private String errMsg;
@ApiModelProperty("数据")
private T data;
}
统一格式输出工具类
package com.chai.utils;
import com.chai.enums.ResultEnum;
import com.chai.model.vo.ResultVO;
/**
* @Auther: chaixuhong
*/
public class ResponseUtil {
/**
* 错误返回
* @param resultEnum
* @return
*/
public static ResultVO error(ResultEnum resultEnum){
ResultVO resultVO = new ResultVO();
resultVO.setCode(resultEnum.getCode());
resultVO.setErrMsg(resultEnum.getErrMsg());
return resultVO;
}
/**
* 自定义返回
* @param code
* @param errMsg
* @return
*/
public static ResultVO error(int code , String errMsg){
ResultVO resultVO = new ResultVO();
resultVO.setCode(code);
resultVO.setErrMsg(errMsg);
return resultVO;
}
/**
* 有参成功返回
* @param data
* @return
*/
public static ResultVO success(Object data){
ResultVO resultVO = new ResultVO();
resultVO.setCode(ResultEnum.SUCCESS.getCode());
resultVO.setErrMsg(ResultEnum.SUCCESS.getErrMsg());
resultVO.setData(data);
return resultVO;
}
/**
* 无参成功返回
* @return
*/
public static ResultVO success(){
ResultVO resultVO = new ResultVO();
resultVO.setCode(ResultEnum.SUCCESS.getCode());
resultVO.setErrMsg(ResultEnum.SUCCESS.getErrMsg());
return resultVO;
}
}
统一格式枚举类
package com.chai.enums;
import lombok.Getter;
@Getter
public enum ResultEnum {
SUCCESS(1, "success"),
SERVER_ERROR(500,"服务器异常!"),
MP_NO_LOGIN(1000,"没有授权登录"),
PARAM_ERROR(1002, "参数错误"),
PARAM_NOT_NULL(1003, "参数不能为空"),
NO_OPERATION_AUTHORITY(1004, "没有权限操作"),
/**********业务状态码*********/
/******授权状态码*****/
TOKEN_MISSING(1000, "token缺失"),
TOKEN_NOT_AFFECTIVE(1000, "token无效"),
NEED_REGISTER(2, "用户没有授权"),
OPENID_IS_NOT_RIGHT(1009,"openid不合法"),
WEIXIN_JS_CODE_OAUTH_ERROR(1005, "微信JS_CODE换取用户信息错误"),
ACCESS_TOKEN_GET_FAIL(1103,"获取accessToken失败!"),
UNIOIN_ID_ERROR(1104,"获取UNIOIN_ID失败!"),
GET_MINI_CODE_ERROR(1105,"获取小程序二维码失败!"),
ACCOUNT_IS_EXIST(1106, "用户账号已存在"),
;
private int code;
private String errMsg;
ResultEnum(int code, String errMsg) {
this.code = code;
this.errMsg = errMsg;
}
}
测试requestParam请求校验
public ResultVO getOrderDetailById(@NotNull(message = "orderId不能为空") Integer orderId) {
LoginUserModel loginUser = UserLocalCache.getUser();
LambdaQueryWrapper<RepairOrder> queryWrapper = new LambdaQueryWrapper();
queryWrapper.eq(RepairOrder::getUserId,loginUser.getUserId())
.eq(RepairOrder::getOrderId,orderId);
return ResponseUtil.success(repairOrderService.getOne(queryWrapper));
}
测试requestBody请求
实体类
package com.chai.model.rev;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
*
* @author chaixuhong
* @since 2021-08-04
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "RepairOrder接收类", description = "")
public class RepairOrderRev implements Serializable {
private static final long serialVersionUID = -6258584086487961208L;
@ApiModelProperty(value = "报修服务")
@NotBlank(message = "报修服务不能为空")
private String repairServerType;
@ApiModelProperty(value = "报修故障")
@NotBlank(message = "报修故障不能为空")
private String repairFault;
@ApiModelProperty(value = "报修区域")
@NotBlank(message = "报修区域不能为空")
private String repairArea;
@ApiModelProperty(value = "报修照片")
private String repairUrl;
@ApiModelProperty(value = "报修备注")
private String repairDesc;
@ApiModelProperty(value = "报修用户预约时间")
@NotNull(message = "预约时间不能为空")
private LocalDateTime userReserveDate;
@ApiModelProperty(value = "报修用户姓名")
@NotBlank(message = "报修用户姓名不能为空")
private String userName;
@ApiModelProperty(value = "报修用户手机号")
@NotBlank(message = "报修用户手机号不能为空")
private String userPhone;
@ApiModelProperty(value = "报修用户地址")
@NotBlank(message = "报修用户地址不能为空")
private String userAddress;
}
controller
@ApiOperation("提交订单")
@PutMapping("/save")
public ResultVO saveOrder(@RequestBody @Validated RepairOrderRev repairOrderRev) {
LoginUserModel loginUser = UserLocalCache.getUser();
RepairOrder repairOrder = new RepairOrder();
BeanUtils.copyProperties(repairOrderRev,repairOrder);
repairOrder.setUserId(loginUser.getUserId());
boolean save = repairOrderService.save(repairOrder);
if (save){
return ResponseUtil.success("保存成功");
}
return ResponseUtil.error(ResultEnum.PARAM_ERROR);
}