mirror of
https://github.moeyy.xyz/https://github.com/GeWuYou/forgeboot
synced 2025-10-27 18:58:55 +08:00
feat(exception): Add a global exception handling module
- Added the WebMvcExceptionProperties class to configure exception handling related properties: Create the WebMvcExceptionAutoConfiguration class for automatic configuration - Add the GlobalException and InternalException classes as the base classes for global and internal exceptions - Implement the GlobalExceptionHandler class to handle all kinds of exceptions in a unified manner - Added internationalization support and created multilingual message files - Configure the Gradle build script and project structure
This commit is contained in:
parent
ff8593007d
commit
1fa28e4c57
3
forgeboot-webmvc/exception/.gitattributes
vendored
Normal file
3
forgeboot-webmvc/exception/.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/gradlew text eol=lf
|
||||
*.bat text eol=crlf
|
||||
*.jar binary
|
||||
40
forgeboot-webmvc/exception/.gitignore
vendored
Normal file
40
forgeboot-webmvc/exception/.gitignore
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
HELP.md
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Kotlin ###
|
||||
.kotlin
|
||||
18
forgeboot-webmvc/exception/build.gradle.kts
Normal file
18
forgeboot-webmvc/exception/build.gradle.kts
Normal file
@ -0,0 +1,18 @@
|
||||
plugins{
|
||||
alias(libs.plugins.forgeboot.i18n.keygen)
|
||||
alias(libs.plugins.kotlin.plugin.spring)
|
||||
}
|
||||
dependencies {
|
||||
implementation(project(Modules.Core.EXTENSION))
|
||||
api(project(Modules.I18n.STARTER))
|
||||
api(project(Modules.TRACE.STARTER))
|
||||
implementation(project(Modules.Webmvc.DTO))
|
||||
compileOnly(libs.springBootStarter.validation)
|
||||
compileOnly(libs.springBootStarter.web)
|
||||
kapt(libs.springBoot.configuration.processor)
|
||||
}
|
||||
i18nKeyGen {
|
||||
rootPackage.set("com.gewuyou.forgeboot.webmvc.extension.i18n")
|
||||
readPath.set("src/main/resources/i18n/${project.name}")
|
||||
level.set(3)
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package com.gewuyou.forgeboot.webmvc.exception.config
|
||||
|
||||
import com.gewuyou.forgeboot.i18n.api.MessageResolver
|
||||
import com.gewuyou.forgeboot.trace.api.RequestIdProvider
|
||||
import com.gewuyou.forgeboot.webmvc.exception.config.entities.WebMvcExceptionProperties
|
||||
import com.gewuyou.forgeboot.webmvc.exception.handler.GlobalExceptionHandler
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
/**
|
||||
*Web MVC 异常自动配置
|
||||
*
|
||||
* @since 2025-05-13 11:48:01
|
||||
* @author gewuyou
|
||||
*/
|
||||
@EnableConfigurationProperties(WebMvcExceptionProperties::class)
|
||||
@Configuration
|
||||
class WebMvcExceptionAutoConfiguration {
|
||||
/**
|
||||
*默认消息解析器
|
||||
*
|
||||
* @since 2025-05-03 16:21:43
|
||||
* @author gewuyou
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
fun defaultMessageResolver(): MessageResolver = MessageResolver { code, _ -> code }
|
||||
|
||||
/**
|
||||
*默认请求ID提供商
|
||||
*
|
||||
* @since 2025-05-03 16:22:18
|
||||
* @author gewuyou
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
fun defaultRequestIdProvider(): RequestIdProvider = RequestIdProvider { "" }
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
fun globalExceptionHandler(
|
||||
webMvcExceptionProperties: WebMvcExceptionProperties,
|
||||
messageResolver: MessageResolver,
|
||||
requestIdProvider: RequestIdProvider,
|
||||
): GlobalExceptionHandler {
|
||||
return GlobalExceptionHandler(
|
||||
webMvcExceptionProperties,
|
||||
messageResolver,
|
||||
requestIdProvider
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package com.gewuyou.forgeboot.webmvc.exception.config.entities
|
||||
|
||||
import com.gewuyou.forgeboot.webmvc.extension.i18n.I18nKeys
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
|
||||
/**
|
||||
* Web Mvc异常属性
|
||||
*
|
||||
* @author gewuyou
|
||||
* @since 2025-05-13 11:06:46
|
||||
*/
|
||||
@ConfigurationProperties("forgeboot.webmvc.exception")
|
||||
class WebMvcExceptionProperties {
|
||||
/**
|
||||
* 设置其他通用外部异常的错误代码
|
||||
*
|
||||
* @param otherGeneralExternalExceptionErrorCode 其他通用外部异常的错误代码
|
||||
*/
|
||||
var otherGeneralExternalExceptionErrorCode: Int = 500
|
||||
|
||||
|
||||
/**
|
||||
* 设置其他通用外部异常的错误消息
|
||||
*
|
||||
* @param otherGeneralExternalExceptionErrorMessage 其他通用外部异常的错误消息
|
||||
*/
|
||||
var otherGeneralExternalExceptionErrorMessage: String = I18nKeys.Forgeboot.Webmvc.Exception.INVALID_SERVER_ERROR
|
||||
|
||||
/**
|
||||
* 设置默认验证异常的错误代码
|
||||
*
|
||||
* @param defaultValidationExceptionErrorCode 默认验证异常的错误代码
|
||||
*/
|
||||
var defaultValidationExceptionErrorCode: Int = 400
|
||||
|
||||
|
||||
/**
|
||||
* 设置默认验证异常的错误消息
|
||||
*
|
||||
* @param defaultValidationExceptionErrorMessage 默认验证异常的错误消息
|
||||
*/
|
||||
var defaultValidationExceptionErrorMessage: String = I18nKeys.Forgeboot.Webmvc.Exception.ILLEGAL_PARAMETER
|
||||
|
||||
/**
|
||||
* 设置默认验证异常的字段错误消息
|
||||
*
|
||||
* @param defaultValidationExceptionFieldErrorMessage 默认验证异常的字段错误消息
|
||||
*/
|
||||
var defaultValidationExceptionFieldErrorMessage: String = I18nKeys.Forgeboot.Webmvc.Exception.ILLEGAL_PARAMETER
|
||||
|
||||
/**
|
||||
* 设置默认无效参数异常的错误代码
|
||||
*
|
||||
* @param defaultInvalidParameterErrorCode 默认无效参数异常的错误代码
|
||||
*/
|
||||
var defaultInvalidParameterErrorCode: Int = 400
|
||||
|
||||
/**
|
||||
* 设置默认无效参数异常的错误消息
|
||||
*
|
||||
* @param defaultInvalidParameterErrorMessage 默认无效参数异常的错误消息
|
||||
*/
|
||||
var defaultInvalidParameterErrorMessage: String = I18nKeys.Forgeboot.Webmvc.Exception.ILLEGAL_PARAMETER
|
||||
|
||||
/**
|
||||
* 设置默认内部服务器错误异常的错误代码
|
||||
*
|
||||
* @param defaultInternalServerErrorCode 默认内部服务器错误异常的错误代码
|
||||
*/
|
||||
var defaultInternalServerErrorCode: Int = 500
|
||||
|
||||
|
||||
/**
|
||||
* 设置默认内部服务器错误异常的错误消息
|
||||
*
|
||||
* @param defaultInternalServerErrorMessage 默认内部服务器错误异常的错误消息
|
||||
*/
|
||||
var defaultInternalServerErrorMessage: String = I18nKeys.Forgeboot.Webmvc.Exception.INVALID_SERVER_ERROR
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package com.gewuyou.forgeboot.webmvc.exception.core
|
||||
|
||||
import com.gewuyou.forgeboot.i18n.api.ResponseInformation
|
||||
import com.gewuyou.forgeboot.i18n.impl.exception.I18nBaseException
|
||||
|
||||
|
||||
/**
|
||||
* 全局异常: 继承I18nBaseException,如果其它模块需要抛出全局异常,则继承此类
|
||||
*
|
||||
* @author gewuyou
|
||||
* @since 2024-11-23 16:45:10
|
||||
*/
|
||||
open class GlobalException : I18nBaseException {
|
||||
/**
|
||||
* 构造函数:初始化全局异常
|
||||
*
|
||||
* @param responseInformation 响应信息,包含错误代码和消息
|
||||
*/
|
||||
constructor(responseInformation: ResponseInformation) : super(responseInformation)
|
||||
|
||||
/**
|
||||
* 构造函数:初始化全局异常,并包含原始异常
|
||||
*
|
||||
* @param responseInformation 响应信息,包含错误代码和消息
|
||||
* @param cause 原始异常
|
||||
*/
|
||||
constructor(responseInformation: ResponseInformation, cause: Throwable) : super(responseInformation, cause)
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package com.gewuyou.forgeboot.webmvc.exception.core
|
||||
|
||||
import com.gewuyou.forgeboot.i18n.api.InternalInformation
|
||||
|
||||
|
||||
/**
|
||||
* 内部异常
|
||||
*
|
||||
* @author gewuyou
|
||||
* @since 2024-11-24 21:14:03
|
||||
*/
|
||||
open class InternalException : RuntimeException {
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
val errorMessage: String
|
||||
|
||||
/**
|
||||
* 可选(国际化内部错误信息码)
|
||||
*/
|
||||
@Transient
|
||||
val internalInformation: InternalInformation?
|
||||
|
||||
/**
|
||||
* 构造一个新的运行时异常,其详细消息为`null`。
|
||||
* 原因尚未初始化,可以随后通过调用[.initCause]进行初始化。
|
||||
*
|
||||
* @param errorMessage 详细消息
|
||||
*/
|
||||
constructor(errorMessage: String) {
|
||||
this.errorMessage = errorMessage
|
||||
this.internalInformation = null
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定的详细消息和原因构造新的运行时异常。
|
||||
*
|
||||
* @param errorMessage 详细消息
|
||||
* @param cause 异常的原因
|
||||
*/
|
||||
constructor(errorMessage: String, cause: Throwable?) : super(errorMessage, cause) {
|
||||
this.errorMessage = errorMessage
|
||||
this.internalInformation = null
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定的详细消息和国际化内部错误信息码构造新的运行时异常。
|
||||
*
|
||||
* @param errorMessage 详细消息
|
||||
* @param internalInformation 国际化内部错误信息码
|
||||
*/
|
||||
constructor(errorMessage: String, internalInformation: InternalInformation?) {
|
||||
this.errorMessage = errorMessage
|
||||
this.internalInformation = internalInformation
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定的详细消息、原因和国际化内部错误信息码构造新的运行时异常。
|
||||
*GlobalExceptionHandler
|
||||
* @param errorMessage 详细消息
|
||||
* @param cause 异常的原因
|
||||
* @param internalInformation 国际化内部错误信息码
|
||||
*/
|
||||
constructor(errorMessage: String, cause: Throwable?, internalInformation: InternalInformation?) : super(
|
||||
errorMessage,
|
||||
cause
|
||||
) {
|
||||
this.errorMessage = errorMessage
|
||||
this.internalInformation = internalInformation
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,165 @@
|
||||
package com.gewuyou.forgeboot.webmvc.exception.handler
|
||||
|
||||
|
||||
import com.gewuyou.forgeboot.core.extension.log
|
||||
import com.gewuyou.forgeboot.i18n.api.MessageResolver
|
||||
import com.gewuyou.forgeboot.trace.api.RequestIdProvider
|
||||
import com.gewuyou.forgeboot.webmvc.dto.R
|
||||
import com.gewuyou.forgeboot.webmvc.exception.config.entities.WebMvcExceptionProperties
|
||||
import com.gewuyou.forgeboot.webmvc.exception.core.GlobalException
|
||||
import com.gewuyou.forgeboot.webmvc.exception.core.InternalException
|
||||
|
||||
|
||||
import jakarta.validation.ConstraintViolationException
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice
|
||||
|
||||
/**
|
||||
* 全局异常处理类
|
||||
*
|
||||
* 该类用于统一处理整个应用中抛出的各种异常,以提供统一的错误响应格式
|
||||
* 它通过使用@RestControllerAdvice注解来标识这是一个全局异常处理类
|
||||
*
|
||||
* @author gewuyou
|
||||
* @since 2024-04-13 上午12:22:18
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
class GlobalExceptionHandler(
|
||||
private val webMvcExceptionProperties: WebMvcExceptionProperties,
|
||||
private val messageResolver: MessageResolver,
|
||||
private val requestIdProvider: RequestIdProvider,
|
||||
) {
|
||||
/**
|
||||
* 异常处理器
|
||||
*
|
||||
* 用于处理除特定异常外的所有其他异常
|
||||
*
|
||||
* @param e 异常
|
||||
* @return 响应
|
||||
* @since 2024/4/13 上午12:29
|
||||
*/
|
||||
@ExceptionHandler(Exception::class)
|
||||
fun handleOtherException(e: Exception): R<String> {
|
||||
log.error("other exception:", e)
|
||||
return R.failure(
|
||||
webMvcExceptionProperties.otherGeneralExternalExceptionErrorCode,
|
||||
webMvcExceptionProperties.otherGeneralExternalExceptionErrorMessage,
|
||||
null, null, messageResolver, requestIdProvider
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 @Valid 和 @Validated 校验失败抛出的 MethodArgumentNotValidException 异常
|
||||
*
|
||||
* 该方法首先尝试返回字段错误信息,如果没有字段错误则尝试返回全局错误信息,
|
||||
* 如果都没有则返回默认的校验异常信息
|
||||
*
|
||||
* @param ex 异常
|
||||
* @return 响应信息
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException::class)
|
||||
fun handleValidationExceptions(ex: MethodArgumentNotValidException): R<String> {
|
||||
// 返回字段错误
|
||||
for (fieldError in ex.bindingResult.fieldErrors) {
|
||||
return R.failure(
|
||||
HttpStatus.BAD_REQUEST.value(),
|
||||
fieldError.defaultMessage ?: webMvcExceptionProperties.defaultValidationExceptionFieldErrorMessage,
|
||||
null,
|
||||
null,
|
||||
messageResolver,
|
||||
requestIdProvider
|
||||
)
|
||||
}
|
||||
// 返回全局错误
|
||||
for (objectError in ex.bindingResult.globalErrors) {
|
||||
return R.failure(
|
||||
HttpStatus.BAD_REQUEST.value(),
|
||||
objectError.defaultMessage ?: webMvcExceptionProperties.defaultValidationExceptionErrorMessage,
|
||||
null,
|
||||
null,
|
||||
messageResolver,
|
||||
requestIdProvider
|
||||
)
|
||||
}
|
||||
return R.failure(
|
||||
webMvcExceptionProperties.defaultValidationExceptionErrorCode,
|
||||
webMvcExceptionProperties.defaultValidationExceptionErrorMessage,
|
||||
null,
|
||||
null,
|
||||
messageResolver,
|
||||
requestIdProvider
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 JSR 303/JSR 380 校验失败抛出的 ConstraintViolationException 异常
|
||||
*
|
||||
* 该方法遍历约束违规信息,并返回第一个错误信息如果存在多个错误,
|
||||
* 否则返回默认的无效参数错误信息
|
||||
*
|
||||
* @param ex 异常
|
||||
* @return 响应信息
|
||||
*/
|
||||
@ExceptionHandler(ConstraintViolationException::class)
|
||||
fun handleConstraintViolationException(ex: ConstraintViolationException): R<String> {
|
||||
for (constraintViolation in ex.constraintViolations) {
|
||||
return R.failure(
|
||||
HttpStatus.BAD_REQUEST.value(), constraintViolation.message,
|
||||
null, null, messageResolver, requestIdProvider
|
||||
)
|
||||
}
|
||||
return R.failure(
|
||||
webMvcExceptionProperties.defaultInvalidParameterErrorCode,
|
||||
webMvcExceptionProperties.defaultInvalidParameterErrorMessage,
|
||||
null,
|
||||
null,
|
||||
messageResolver,
|
||||
requestIdProvider
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
*
|
||||
* 用于处理全局异常(GlobalException),返回错误代码和国际化消息代码
|
||||
*
|
||||
* @param e 异常
|
||||
* @return 返回的结果
|
||||
* @since 2024/4/13 下午1:56
|
||||
*/
|
||||
@ExceptionHandler(GlobalException::class)
|
||||
fun handleGlobalException(e: GlobalException): R<String> {
|
||||
return R.failure(
|
||||
e.errorCode, e.errorI18nMessageCode,
|
||||
null, e.errorI18nMessageArgs, messageResolver, requestIdProvider
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部异常处理器
|
||||
*
|
||||
* 用于处理内部异常(InternalException),记录异常信息并返回默认的内部服务器错误信息
|
||||
*
|
||||
* @param e 异常
|
||||
* @return 返回的结果
|
||||
*/
|
||||
@ExceptionHandler(InternalException::class)
|
||||
fun handleGlobalException(e: InternalException): R<String> {
|
||||
log.error("内部异常: 异常信息: {}", e.errorMessage, e)
|
||||
e.internalInformation?.responseI8nMessageCode?.let {
|
||||
log.error(
|
||||
"i18nMessage: {}", messageResolver.resolve(it, e.internalInformation.responseI8nMessageArgs)
|
||||
)
|
||||
}
|
||||
return R.failure(
|
||||
webMvcExceptionProperties.defaultInternalServerErrorCode,
|
||||
webMvcExceptionProperties.defaultInternalServerErrorMessage,
|
||||
null,
|
||||
null,
|
||||
messageResolver,
|
||||
requestIdProvider
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
forgeboot.webmvc.exception.invalid_server_error=内部执行错误,请将本次请求的请求id反馈\!
|
||||
forgeboot.webmvc.exception.illegal_parameter=非法参数,请检查输入\!
|
||||
@ -0,0 +1,2 @@
|
||||
forgeboot.webmvc.exception.invalid_server_error=If there is an internal execution error, please report the request ID of this request\!
|
||||
forgeboot.webmvc.exception.illegal_parameter=Illegal parameters, please check the input\!
|
||||
@ -0,0 +1,2 @@
|
||||
forgeboot.webmvc.exception.invalid_server_error=内部执行错误,请将本次请求的请求id反馈\!
|
||||
forgeboot.webmvc.exception.illegal_parameter=非法参数,请检查输入\!
|
||||
Loading…
x
Reference in New Issue
Block a user