package com.jilongda.common.exception; import com.jilongda.common.basic.ApiResult; import com.jilongda.common.swagger.GlobalResultEnum; import lombok.extern.slf4j.Slf4j; import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.multipart.MultipartException; import org.springframework.web.servlet.NoHandlerFoundException; import javax.naming.SizeLimitExceededException; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.xml.bind.ValidationException; import java.nio.file.AccessDeniedException; import java.text.DecimalFormat; import java.util.Set; /** * @author madman * @version 1.0 * @date 2019/7/30 16:13 * 全局的异常处理 */ @RestControllerAdvice @Slf4j public class GlobalExceptionHandler { /** * 请求参数错误 */ private final static String BASE_PARAM_ERR_MSG = "参数校验不通过"; @Value("${spring.servlet.multipart.max-file-size:10MB}") private String maxFileSize; @Value("${spring.servlet.multipart.max-request-size:10MB}") private String maxRequestSize; /** * 文件大小转换 * * @param size * @return */ private static String asReadableFileSize(long size) { if (size <= 0) { return "0"; } final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"}; int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + "" + units[digitGroups]; } /** * 顶级的异常处理 * 处理所有不可知异常 * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler(Exception.class) public ApiResult handleException(Exception e) { // 打印异常堆栈信息 log.error("顶级异常->:" + e.getMessage(), e); if (e instanceof TokenException) { TokenException e1 = (TokenException) e; return ApiResult.failed(e1.getCode(), e.getMessage()); } return ApiResult.failed(e.getMessage()); } @ResponseStatus(HttpStatus.OK) @ExceptionHandler({MultipartException.class}) public ApiResult fileUpLoad2(MaxUploadSizeExceededException e) { log.error("上传文件异常 => : {}", e.getMessage()); if (e.getCause().getCause() instanceof FileSizeLimitExceededException) { return ApiResult.failed("文件大小超出限制,允许的大小在" + maxFileSize); } else if (e.getCause().getCause() instanceof SizeLimitExceededException) { return ApiResult.failed("文件单次上传总大小超过限制,允许的大小在" + maxRequestSize); } else { return ApiResult.failed("文件上传失败"); } } /** * 自定义的异常处理 * 业务异常 * * @param e * @return */ @ExceptionHandler(ServerException.class) @ResponseStatus(HttpStatus.OK) public ApiResult handleBusinessException(ServerException e) { // 打印异常堆栈信息 log.error(" ---> 业务处理异常: {}", e.getMessage()); return ApiResult.failed(e.getCode(), e.getMessage(), e.getData()); } /** * 请求参数类型不匹配异常 * 例如本应传递int,却传递了String * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({MethodArgumentTypeMismatchException.class}) public ApiResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { log.warn("方法参数类型不匹配异常: {}", e); return ApiResult.failed("方法请求参数类型不匹配异常"); } /** * 缺少servlet请求参数抛出的异常 * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({MissingServletRequestParameterException.class}) public ApiResult handleMissingServletRequestParameterException(MissingServletRequestParameterException e) { log.warn(" 参数错误: {}", e.getParameterName()); return ApiResult.failed(BASE_PARAM_ERR_MSG + ":请求参数"); } /** * 请求参数不能正确读取解析时,抛出的异常,比如传入和接受的参数类型不一致 * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({HttpMessageNotReadableException.class}) public ApiResult handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { log.error("参数解析失败:", e); return ApiResult.failed("传入和接受的参数类型不一致"); } /** * 请求参数无效抛出的异常 * 校验参数抛出的异常 * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({MethodArgumentNotValidException.class}) public ApiResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { BindingResult result = e.getBindingResult(); String message = getBindResultMessage(result); log.error("参数验证失败:" + message); return ApiResult.failed(message.split(":")[1]); } /** * 构造参数校验错误信息 * * @param result * @return */ private String getBindResultMessage(BindingResult result) { // 多个错误随机取值的 FieldError error = result.getFieldError(); String field = error != null ? error.getField() : "空"; String code = error != null ? error.getDefaultMessage() : "空"; return String.format("%s:%s", field, code); } /** * 请求参数绑定到controller请求参数时的异常 * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({BindException.class}) public ApiResult handleHttpMessageNotReadableException(BindException e) { BindingResult result = e.getBindingResult(); String message = getBindResultMessage(result); log.error("参数绑定失败:{}", message); return ApiResult.failed("请求参数绑定失败"); } /** * javax.validation:validation-api 校验参数抛出的异常 * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({ConstraintViolationException.class}) public ApiResult handleServiceException(ConstraintViolationException e) { Set> violations = e.getConstraintViolations(); ConstraintViolation violation = violations.iterator().next(); String message = violation.getMessage(); log.warn("参数验证失败:{}", message); return ApiResult.failed("参数验证失败:" + message); } /** * javax.validation 下校验参数时抛出的异常 * 400 - Bad Request * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({ValidationException.class}) public ApiResult handleValidationException(ValidationException e) { log.warn("参数验证失败:{}", e); return ApiResult.failed("参数验证失败:" + e.getMessage()); } /** * 不支持该请求方法时抛出的异常 * 例如本应GET,结果却是POST * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({HttpRequestMethodNotSupportedException.class}) public ApiResult handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { log.warn("不支持当前请求方法: {}", e); return ApiResult.failed("Request Method 不正确"); } /** * 不支持当前媒体类型抛出的异常 * * @param e * @return */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler({HttpMediaTypeNotSupportedException.class}) public ApiResult handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) { log.warn("不支持当前媒体类型:{} ", e); return ApiResult.failed("不支持当前媒体类型"); } /** * 操作数据库出现异常:名称重复,外键关联 */ @ResponseStatus(HttpStatus.OK) @ExceptionHandler(DataIntegrityViolationException.class) public ApiResult handleException(DataIntegrityViolationException e) { log.error("操作数据库出现异常:{}", e); return ApiResult.failed("操作数据库出现异常:" + e.getMessage()); } @ResponseStatus(HttpStatus.OK) @ExceptionHandler(NoHandlerFoundException.class) public ApiResult handlerNoFoundException(Exception e) { log.error("请求路径匹配异常:" + e.getMessage(), e); return ApiResult.failed(GlobalResultEnum.NOT_FOUND.getCode(), "路径不存在,请检查路径是否正确"); } @ResponseStatus(HttpStatus.OK) @ExceptionHandler(AccessDeniedException.class) public ApiResult handleAuthorizationException(AccessDeniedException e) { log.warn(e.getMessage()); return ApiResult.failed(GlobalResultEnum.FORBIDDEN.getCode(), "没有权限,请联系管理员授权"); } }