|
@@ -2,18 +2,20 @@ package cc.uncarbon.framework.web.config;
|
|
|
|
|
|
|
|
import cc.uncarbon.framework.core.constant.HelioConstant;
|
|
import cc.uncarbon.framework.core.constant.HelioConstant;
|
|
|
import cc.uncarbon.framework.core.exception.BusinessException;
|
|
import cc.uncarbon.framework.core.exception.BusinessException;
|
|
|
|
|
+import cc.uncarbon.framework.core.props.HelioProperties;
|
|
|
|
|
+import cc.uncarbon.framework.i18n.util.I18nUtil;
|
|
|
|
|
+import cc.uncarbon.framework.web.enums.GlobalWebExceptionI18nMessageEnum;
|
|
|
import cc.uncarbon.framework.web.model.response.ApiResult;
|
|
import cc.uncarbon.framework.web.model.response.ApiResult;
|
|
|
import cc.uncarbon.framework.web.util.InvalidFieldUtil;
|
|
import cc.uncarbon.framework.web.util.InvalidFieldUtil;
|
|
|
import cn.dev33.satoken.exception.NotLoginException;
|
|
import cn.dev33.satoken.exception.NotLoginException;
|
|
|
import cn.dev33.satoken.exception.NotPermissionException;
|
|
import cn.dev33.satoken.exception.NotPermissionException;
|
|
|
import cn.dev33.satoken.exception.NotRoleException;
|
|
import cn.dev33.satoken.exception.NotRoleException;
|
|
|
import com.fasterxml.jackson.core.JsonParseException;
|
|
import com.fasterxml.jackson.core.JsonParseException;
|
|
|
-import java.nio.charset.StandardCharsets;
|
|
|
|
|
-import javax.servlet.http.HttpServletRequest;
|
|
|
|
|
-import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
|
|
+import lombok.NonNull;
|
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
|
|
-import org.springframework.core.annotation.Order;
|
|
|
|
|
|
|
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
|
|
import org.springframework.http.HttpStatus;
|
|
import org.springframework.http.HttpStatus;
|
|
|
import org.springframework.http.MediaType;
|
|
import org.springframework.http.MediaType;
|
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.http.ResponseEntity;
|
|
@@ -28,6 +30,9 @@ import org.springframework.web.bind.annotation.RestController;
|
|
|
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
|
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
|
|
import org.springframework.web.servlet.NoHandlerFoundException;
|
|
import org.springframework.web.servlet.NoHandlerFoundException;
|
|
|
|
|
|
|
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Web 全局异常处理自动配置类
|
|
* Web 全局异常处理自动配置类
|
|
|
*
|
|
*
|
|
@@ -37,12 +42,13 @@ import org.springframework.web.servlet.NoHandlerFoundException;
|
|
|
@RestController
|
|
@RestController
|
|
|
@ControllerAdvice
|
|
@ControllerAdvice
|
|
|
@AutoConfiguration
|
|
@AutoConfiguration
|
|
|
-// 主动标注为最低优先级,便于覆盖
|
|
|
|
|
-@Order
|
|
|
|
|
-public class AdviceExceptionAutoConfiguration {
|
|
|
|
|
|
|
+@ConditionalOnMissingBean(value = GlobalWebExceptionHandlerAutoConfiguration.class)
|
|
|
|
|
+@RequiredArgsConstructor
|
|
|
|
|
+public class GlobalWebExceptionHandlerAutoConfiguration {
|
|
|
|
|
|
|
|
protected static final MediaType MEDIA_TYPE = new MediaType("application", "json", StandardCharsets.UTF_8);
|
|
protected static final MediaType MEDIA_TYPE = new MediaType("application", "json", StandardCharsets.UTF_8);
|
|
|
protected static final String DUBBO_PACKAGE_PREFIX = "org.apache.dubbo";
|
|
protected static final String DUBBO_PACKAGE_PREFIX = "org.apache.dubbo";
|
|
|
|
|
+ private final HelioProperties helioProperties;
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -50,9 +56,22 @@ public class AdviceExceptionAutoConfiguration {
|
|
|
*/
|
|
*/
|
|
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
|
|
@ExceptionHandler({BusinessException.class})
|
|
@ExceptionHandler({BusinessException.class})
|
|
|
- public ResponseEntity<ApiResult<?>> handleBusinessException(BusinessException e, HttpServletRequest request, HttpServletResponse response) {
|
|
|
|
|
|
|
+ public ResponseEntity<ApiResult<?>> handleBusinessException(BusinessException e, HttpServletRequest request) {
|
|
|
this.logError(e, request);
|
|
this.logError(e, request);
|
|
|
- ApiResult<?> ret = ApiResult.fail(e.getCode(), e.getMessage());
|
|
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ @since 1.7.2,国际化支持;详见文档:进阶使用-国际化
|
|
|
|
|
+ */
|
|
|
|
|
+ String msg;
|
|
|
|
|
+ if (helioProperties.getI18n().getEnabled() && e.getCustomEnumField() != null) {
|
|
|
|
|
+ // 如果启用了国际化,并制定了枚举值,则按国际化翻译值显示
|
|
|
|
|
+ msg = I18nUtil.messageOf(e.getCustomEnumField(), e.getTemplateParams());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 按已有值显示
|
|
|
|
|
+ msg = e.getMessage();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ApiResult<?> ret = ApiResult.fail(e.getCode(), msg);
|
|
|
return createResponseEntity(HttpStatus.BAD_REQUEST, ret);
|
|
return createResponseEntity(HttpStatus.BAD_REQUEST, ret);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -63,7 +82,9 @@ public class AdviceExceptionAutoConfiguration {
|
|
|
@ExceptionHandler({NotLoginException.class})
|
|
@ExceptionHandler({NotLoginException.class})
|
|
|
public ResponseEntity<ApiResult<?>> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
|
public ResponseEntity<ApiResult<?>> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
|
|
this.logError(e, request);
|
|
this.logError(e, request);
|
|
|
- ApiResult<?> ret = ApiResult.fail(HttpStatus.UNAUTHORIZED.value(), "请您先登录");
|
|
|
|
|
|
|
+
|
|
|
|
|
+ GlobalWebExceptionI18nMessageEnum msgEnum = GlobalWebExceptionI18nMessageEnum.GLOBAL__NO_LOGIN;
|
|
|
|
|
+ ApiResult<?> ret = ApiResult.fail(HttpStatus.UNAUTHORIZED.value(), this.getI18nMessage(msgEnum));
|
|
|
return createResponseEntity(HttpStatus.UNAUTHORIZED, ret);
|
|
return createResponseEntity(HttpStatus.UNAUTHORIZED, ret);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -74,18 +95,22 @@ public class AdviceExceptionAutoConfiguration {
|
|
|
@ExceptionHandler({NotPermissionException.class})
|
|
@ExceptionHandler({NotPermissionException.class})
|
|
|
public ResponseEntity<ApiResult<?>> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
|
|
public ResponseEntity<ApiResult<?>> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
|
|
|
this.logError(e, request);
|
|
this.logError(e, request);
|
|
|
- ApiResult<?> ret = ApiResult.fail(HttpStatus.FORBIDDEN.value(), "您权限不足");
|
|
|
|
|
|
|
+
|
|
|
|
|
+ GlobalWebExceptionI18nMessageEnum msgEnum = GlobalWebExceptionI18nMessageEnum.GLOBAL__PERMISSION_NOT_MATCH;
|
|
|
|
|
+ ApiResult<?> ret = ApiResult.fail(HttpStatus.FORBIDDEN.value(), this.getI18nMessage(msgEnum));
|
|
|
return createResponseEntity(HttpStatus.FORBIDDEN, ret);
|
|
return createResponseEntity(HttpStatus.FORBIDDEN, ret);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 用户身份异常
|
|
|
|
|
|
|
+ * 用户角色异常
|
|
|
*/
|
|
*/
|
|
|
@ResponseStatus(HttpStatus.FORBIDDEN)
|
|
@ResponseStatus(HttpStatus.FORBIDDEN)
|
|
|
@ExceptionHandler({NotRoleException.class})
|
|
@ExceptionHandler({NotRoleException.class})
|
|
|
public ResponseEntity<ApiResult<?>> handleNotRoleException(NotRoleException e, HttpServletRequest request) {
|
|
public ResponseEntity<ApiResult<?>> handleNotRoleException(NotRoleException e, HttpServletRequest request) {
|
|
|
this.logError(e, request);
|
|
this.logError(e, request);
|
|
|
- ApiResult<?> ret = ApiResult.fail(HttpStatus.FORBIDDEN.value(), "您与要求角色不符");
|
|
|
|
|
|
|
+
|
|
|
|
|
+ GlobalWebExceptionI18nMessageEnum msgEnum = GlobalWebExceptionI18nMessageEnum.GLOBAL__ROLE_NOT_MATCH;
|
|
|
|
|
+ ApiResult<?> ret = ApiResult.fail(HttpStatus.FORBIDDEN.value(), this.getI18nMessage(msgEnum));
|
|
|
return createResponseEntity(HttpStatus.FORBIDDEN, ret);
|
|
return createResponseEntity(HttpStatus.FORBIDDEN, ret);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -96,7 +121,9 @@ public class AdviceExceptionAutoConfiguration {
|
|
|
@ExceptionHandler({NoHandlerFoundException.class})
|
|
@ExceptionHandler({NoHandlerFoundException.class})
|
|
|
public ResponseEntity<ApiResult<?>> handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletRequest request) {
|
|
public ResponseEntity<ApiResult<?>> handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletRequest request) {
|
|
|
this.logError(e, request);
|
|
this.logError(e, request);
|
|
|
- ApiResult<?> ret = ApiResult.fail(HttpStatus.NOT_FOUND.value(), "你迷路啦");
|
|
|
|
|
|
|
+
|
|
|
|
|
+ GlobalWebExceptionI18nMessageEnum msgEnum = GlobalWebExceptionI18nMessageEnum.GLOBAL__NOT_FOUND;
|
|
|
|
|
+ ApiResult<?> ret = ApiResult.fail(HttpStatus.NOT_FOUND.value(), this.getI18nMessage(msgEnum));
|
|
|
return createResponseEntity(HttpStatus.NOT_FOUND, ret);
|
|
return createResponseEntity(HttpStatus.NOT_FOUND, ret);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -104,10 +131,10 @@ public class AdviceExceptionAutoConfiguration {
|
|
|
* JsonParseException, HttpMessageNotReadableException
|
|
* JsonParseException, HttpMessageNotReadableException
|
|
|
* Jackson反序列化异常
|
|
* Jackson反序列化异常
|
|
|
* 通常是因为JSON格式错误,或枚举输入值超出范围
|
|
* 通常是因为JSON格式错误,或枚举输入值超出范围
|
|
|
- *
|
|
|
|
|
|
|
+ * <p>
|
|
|
* IllegalArgumentException
|
|
* IllegalArgumentException
|
|
|
* 不合法的参数异常
|
|
* 不合法的参数异常
|
|
|
- *
|
|
|
|
|
|
|
+ * <p>
|
|
|
* MethodArgumentTypeMismatchException
|
|
* MethodArgumentTypeMismatchException
|
|
|
* "@PathVariable" 注解收参类型为 Long,但传的是 String
|
|
* "@PathVariable" 注解收参类型为 Long,但传的是 String
|
|
|
*/
|
|
*/
|
|
@@ -115,8 +142,10 @@ public class AdviceExceptionAutoConfiguration {
|
|
|
@ExceptionHandler({JsonParseException.class, HttpMessageNotReadableException.class, IllegalArgumentException.class, MethodArgumentTypeMismatchException.class})
|
|
@ExceptionHandler({JsonParseException.class, HttpMessageNotReadableException.class, IllegalArgumentException.class, MethodArgumentTypeMismatchException.class})
|
|
|
public ResponseEntity<ApiResult<?>> handleJsonParseException(Exception e, HttpServletRequest request) {
|
|
public ResponseEntity<ApiResult<?>> handleJsonParseException(Exception e, HttpServletRequest request) {
|
|
|
this.logError(e, request);
|
|
this.logError(e, request);
|
|
|
- ApiResult<?> ret = ApiResult.fail(HttpStatus.NOT_ACCEPTABLE.value(), "错误参数格式或值");
|
|
|
|
|
- return createResponseEntity(HttpStatus.BAD_REQUEST, ret);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ GlobalWebExceptionI18nMessageEnum msgEnum = GlobalWebExceptionI18nMessageEnum.GLOBAL__UNACCEPTABLE_PARAMETERS;
|
|
|
|
|
+ ApiResult<?> ret = ApiResult.fail(HttpStatus.NOT_ACCEPTABLE.value(), this.getI18nMessage(msgEnum));
|
|
|
|
|
+ return createResponseEntity(HttpStatus.NOT_ACCEPTABLE, ret);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -127,8 +156,10 @@ public class AdviceExceptionAutoConfiguration {
|
|
|
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
|
|
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
|
|
|
public ResponseEntity<ApiResult<?>> handleBindException(BindException e, HttpServletRequest request) {
|
|
public ResponseEntity<ApiResult<?>> handleBindException(BindException e, HttpServletRequest request) {
|
|
|
this.logError(e, request);
|
|
this.logError(e, request);
|
|
|
- ApiResult<?> ret = ApiResult.fail(HttpStatus.NOT_ACCEPTABLE.value(), "错误参数格式或值", InvalidFieldUtil.getInvalidField(e.getBindingResult()));
|
|
|
|
|
- return createResponseEntity(HttpStatus.BAD_REQUEST, ret);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ GlobalWebExceptionI18nMessageEnum msgEnum = GlobalWebExceptionI18nMessageEnum.GLOBAL__UNACCEPTABLE_PARAMETERS;
|
|
|
|
|
+ ApiResult<?> ret = ApiResult.fail(HttpStatus.NOT_ACCEPTABLE.value(), this.getI18nMessage(msgEnum), InvalidFieldUtil.getInvalidField(e.getBindingResult()));
|
|
|
|
|
+ return createResponseEntity(HttpStatus.NOT_ACCEPTABLE, ret);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -138,7 +169,9 @@ public class AdviceExceptionAutoConfiguration {
|
|
|
@ExceptionHandler({HttpRequestMethodNotSupportedException.class})
|
|
@ExceptionHandler({HttpRequestMethodNotSupportedException.class})
|
|
|
public ResponseEntity<ApiResult<?>> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
|
|
public ResponseEntity<ApiResult<?>> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
|
|
|
this.logError(e, request);
|
|
this.logError(e, request);
|
|
|
- ApiResult<?> ret = ApiResult.fail(HttpStatus.METHOD_NOT_ALLOWED.value(), "错误的请求方式");
|
|
|
|
|
|
|
+
|
|
|
|
|
+ GlobalWebExceptionI18nMessageEnum msgEnum = GlobalWebExceptionI18nMessageEnum.GLOBAL__METHOD_NOT_ALLOWED;
|
|
|
|
|
+ ApiResult<?> ret = ApiResult.fail(HttpStatus.METHOD_NOT_ALLOWED.value(), this.getI18nMessage(msgEnum));
|
|
|
return createResponseEntity(HttpStatus.METHOD_NOT_ALLOWED, ret);
|
|
return createResponseEntity(HttpStatus.METHOD_NOT_ALLOWED, ret);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -160,7 +193,8 @@ public class AdviceExceptionAutoConfiguration {
|
|
|
responseCode = HelioConstant.Dubbo.RPC_EXCEPTION_RESPONSE_CODE;
|
|
responseCode = HelioConstant.Dubbo.RPC_EXCEPTION_RESPONSE_CODE;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- ApiResult<?> ret = ApiResult.fail(responseCode, "请稍后再试");
|
|
|
|
|
|
|
+ GlobalWebExceptionI18nMessageEnum msgEnum = GlobalWebExceptionI18nMessageEnum.GLOBAL__INTERNAL_ERROR;
|
|
|
|
|
+ ApiResult<?> ret = ApiResult.fail(responseCode, this.getI18nMessage(msgEnum));
|
|
|
return createResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR, ret);
|
|
return createResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR, ret);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -171,4 +205,16 @@ public class AdviceExceptionAutoConfiguration {
|
|
|
protected static ResponseEntity<ApiResult<?>> createResponseEntity(HttpStatus httpStatus, ApiResult<?> body) {
|
|
protected static ResponseEntity<ApiResult<?>> createResponseEntity(HttpStatus httpStatus, ApiResult<?> body) {
|
|
|
return ResponseEntity.status(httpStatus.value()).contentType(MEDIA_TYPE).body(body);
|
|
return ResponseEntity.status(httpStatus.value()).contentType(MEDIA_TYPE).body(body);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 取国际化翻译值或默认消息,取决于是否实际启用了国际化功能
|
|
|
|
|
+ * @param msgEnum 全局异常处理国际化消息枚举
|
|
|
|
|
+ * @return 消息文本
|
|
|
|
|
+ */
|
|
|
|
|
+ protected String getI18nMessage(@NonNull GlobalWebExceptionI18nMessageEnum msgEnum) {
|
|
|
|
|
+ if (helioProperties.getI18n().getEnabled()) {
|
|
|
|
|
+ return I18nUtil.messageOf(msgEnum.i18nCode(), msgEnum.getDefaultValue());
|
|
|
|
|
+ }
|
|
|
|
|
+ return msgEnum.getDefaultValue();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|