com.github.zhujk.resp.core.CommonAdvice.kt Maven / Gradle / Ivy
package com.github.zhujk.resp.core
import com.github.zhujk.resp.annotation.RespIgnore
import com.github.zhujk.resp.converter.RespConverter
import com.github.zhujk.resp.exception.RespException
import com.github.zhujk.resp.exception.rootCause
import com.github.zhujk.resp.props.RespProperties
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.InitializingBean
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties
import org.springframework.boot.autoconfigure.web.ServerProperties
import org.springframework.core.MethodParameter
import org.springframework.core.annotation.AnnotationUtils
import org.springframework.core.env.Environment
import org.springframework.http.MediaType
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.http.converter.json.MappingJacksonValue
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.http.server.ServletServerHttpRequest
import org.springframework.http.server.ServletServerHttpResponse
import org.springframework.util.AntPathMatcher
import org.springframework.util.Assert
import org.springframework.util.PathMatcher
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
@RestControllerAdvice
class CommonAdvice(
private val respProps: RespProperties,
private val converter: RespConverter<*>,
private val serverProps: ServerProperties,
private val managementServerProps: ManagementServerProperties,
private val environment: Environment
) : ResponseBodyAdvice, InitializingBean {
private val logger = LoggerFactory.getLogger(javaClass)
private val pathMatcher: PathMatcher = AntPathMatcher()
override fun afterPropertiesSet() {
val respClass = respProps.respClass
val convertedClass = converter.convert(0, "")!!.javaClass
Assert.isAssignable(respClass, convertedClass) {
"invalid converter:${converter.javaClass} for resp class:$respClass, converted result class:$convertedClass"
}
}
@ExceptionHandler
fun handleException(t: Throwable): Any? {
when (t) {
is MethodArgumentNotValidException -> {
val msg = t
.bindingResult
.allErrors
.map { it.defaultMessage }
.joinToString(";")
logger.info("invalid args,msg:{}", msg)
return converter.convert(400, msg, throwable = t)
}
is RespException -> {
val code = t.code
val message = t.msg
logger.info("resp error,code:{},message:{}", code, message)
return converter.convert(code, message, throwable = t)
}
else -> {
logger.error("error", t)
val rootCause = t.rootCause
if (rootCause is RespException) {
return converter.convert(rootCause.code, rootCause.msg)
}
return converter.convert(500, INTERNAL_ERROR, throwable = t)
}
}
}
override fun supports(returnType: MethodParameter, converterType: Class>): Boolean {
return AnnotationUtils.findAnnotation(returnType.method!!, RespIgnore::class.java) == null &&
AnnotationUtils.findAnnotation(returnType.declaringClass, RespIgnore::class.java) == null &&
!respProps.respClass.isAssignableFrom(returnType.method!!.returnType)
}
override fun beforeBodyWrite(
body: Any?,
returnType: MethodParameter,
selectedContentType: MediaType,
selectedConverterType: Class>,
request: ServerHttpRequest,
response: ServerHttpResponse
): Any? {
val container = getOrCreateContainer(body)
beforeBodyWriteInternal(container, selectedContentType, returnType, request, response)
return container
}
protected fun getOrCreateContainer(body: Any?): MappingJacksonValue {
return if (body is MappingJacksonValue) body else MappingJacksonValue(body!!)
}
protected fun beforeBodyWriteInternal(
bodyContainer: MappingJacksonValue,
contentType: MediaType,
returnType: MethodParameter,
request: ServerHttpRequest,
response: ServerHttpResponse
) {
val serverContextPath = serverProps.servlet.contextPath ?: ""
val managementContextPath = managementServerProps.servlet.contextPath ?: ""
val serverPort = environment.getRequiredProperty("local.server.port").toInt()
val managementPort = environment.getRequiredProperty("local.management.port").toInt()
val req = (request as ServletServerHttpRequest).servletRequest
val localPort = req.localPort
val requestURI = req.requestURI
val path = requestURI.substringAfter(
when (localPort) {
serverPort -> serverContextPath
managementPort -> managementContextPath
else -> TODO("invalid: localPort:$localPort,serverPort:$serverPort,managementPort:$managementPort")
}
)
if (isIgnoredPath(path)) {
return
}
val data = bodyContainer.value
if (isIgnoredInstance(data)) {
return
}
val status = (response as ServletServerHttpResponse).servletResponse.status
bodyContainer.value = converter.convert(status, SUCCESS, data)!!
}
private fun isIgnoredInstance(data: Any) =
respProps.respClass.isInstance(data) || respProps.ignoredClasses.any { it.isInstance(data) }
private fun isIgnoredPath(path: String) =
respProps.defaultIgnorePathPatterns.any { pathMatcher.match(it, path) } ||
respProps.extraIgnorePathPatterns.any { pathMatcher.match(it, path) }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy