Merge pull request #14 from TouchInstinct/response-wrapper-spring-web
add response-wrapper-spring-web
This commit is contained in:
commit
07d00bc65e
|
|
@ -83,3 +83,7 @@ Interceptor для логирования запросов/ответов.
|
|||
## version-spring-web
|
||||
|
||||
Добавляет возможность задавать версию апи через `properties` без необходимости явно указывать в каждом маппинге
|
||||
|
||||
## response-wrapper-spring-web
|
||||
|
||||
Добавляет обертку для успешного ответа
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
plugins {
|
||||
id("kotlin")
|
||||
id("kotlin-spring")
|
||||
id("maven-publish")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
@file:Suppress("unused")
|
||||
package ru.touchin.wrapper
|
||||
|
||||
import org.springframework.context.annotation.Import
|
||||
import ru.touchin.wrapper.configurations.SpringResponseWrapper
|
||||
|
||||
@Import(value = [SpringResponseWrapper::class])
|
||||
annotation class EnableSpringResponseWrapper
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
@file:Suppress("unused")
|
||||
package ru.touchin.wrapper.advices
|
||||
|
||||
import org.springframework.core.MethodParameter
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.converter.HttpMessageConverter
|
||||
import org.springframework.http.server.ServerHttpRequest
|
||||
import org.springframework.http.server.ServerHttpResponse
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
|
||||
import ru.touchin.wrapper.annotations.NoResponseWrap
|
||||
import ru.touchin.wrapper.annotations.ResponseWrap
|
||||
import ru.touchin.wrapper.components.ResponseBodyWrapper
|
||||
|
||||
@RestControllerAdvice(annotations = [ResponseWrap::class])
|
||||
class WrapResponseAdvice(
|
||||
private val responseWrapper: ResponseBodyWrapper
|
||||
): ResponseBodyAdvice<Any> {
|
||||
|
||||
override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean {
|
||||
return !returnType.hasMethodAnnotation(NoResponseWrap::class.java)
|
||||
}
|
||||
|
||||
/***
|
||||
* Не будет работать, если контроллер возвращает тип String, так как по умолчанию будет выбираться конвертер для строки.
|
||||
* Решить проблему можно так: https://stackoverflow.com/questions/44121648/controlleradvice-responsebodyadvice-failed-to-enclose-a-string-response
|
||||
*/
|
||||
override fun beforeBodyWrite(
|
||||
body: Any?,
|
||||
returnType: MethodParameter,
|
||||
selectedContentType: MediaType,
|
||||
selectedConverterType: Class<out HttpMessageConverter<*>>,
|
||||
request: ServerHttpRequest,
|
||||
response: ServerHttpResponse
|
||||
): Any {
|
||||
return responseWrapper.wrap(body)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package ru.touchin.wrapper.annotations
|
||||
|
||||
@Target(allowedTargets = [AnnotationTarget.FUNCTION, AnnotationTarget.CLASS])
|
||||
annotation class NoResponseWrap
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package ru.touchin.wrapper.annotations
|
||||
|
||||
@Target(allowedTargets = [AnnotationTarget.FUNCTION, AnnotationTarget.CLASS])
|
||||
annotation class ResponseWrap
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package ru.touchin.wrapper.components
|
||||
|
||||
interface ResponseBodyWrapper {
|
||||
|
||||
fun wrap(body: Any?): Any
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
@file:Suppress("unused")
|
||||
package ru.touchin.wrapper.components
|
||||
|
||||
import org.springframework.stereotype.Component
|
||||
import ru.touchin.wrapper.dto.DefaultWrapper
|
||||
import ru.touchin.wrapper.dto.Wrapper
|
||||
|
||||
@Component
|
||||
class ResponseBodyWrapperImpl : ResponseBodyWrapper {
|
||||
|
||||
override fun wrap(body: Any?): Any {
|
||||
if (body is Wrapper) {
|
||||
return body
|
||||
}
|
||||
|
||||
return DefaultWrapper(
|
||||
result = body,
|
||||
errorCode = 0
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.touchin.wrapper.configurations
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
@Suppress("SpringFacetCodeInspection")
|
||||
@Configuration
|
||||
@ComponentScan("ru.touchin.wrapper.advices", "ru.touchin.wrapper.components")
|
||||
class SpringResponseWrapper
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.touchin.wrapper.dto
|
||||
|
||||
@Suppress("unused")
|
||||
class DefaultWrapper(
|
||||
override val result: Any?,
|
||||
val errorCode: Int,
|
||||
val errorMessage: String? = null
|
||||
): Wrapper
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package ru.touchin.wrapper.dto
|
||||
|
||||
interface Wrapper {
|
||||
val result: Any?
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.touchin.web
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import ru.touchin.wrapper.EnableSpringResponseWrapper
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableSpringResponseWrapper
|
||||
class TestApplication
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package ru.touchin.web.wrapper
|
||||
|
||||
import org.hamcrest.Matchers
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import ru.touchin.wrapper.annotations.NoResponseWrap
|
||||
import ru.touchin.wrapper.annotations.ResponseWrap
|
||||
|
||||
@RestController
|
||||
@ResponseWrap
|
||||
@RequestMapping("/wrapper")
|
||||
class WrapperController {
|
||||
|
||||
@GetMapping("/wrap")
|
||||
fun wrap(): Map<String, String> {
|
||||
return mapOf("wrap" to "yes")
|
||||
}
|
||||
|
||||
@NoResponseWrap
|
||||
@GetMapping("/no-wrap")
|
||||
fun noWrap(): Map<String, String> {
|
||||
return mapOf("wrap" to "no")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ActiveProfiles("test")
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
internal class WrapResponseAdviceMvcTest {
|
||||
|
||||
@Autowired
|
||||
private lateinit var mockMvc: MockMvc
|
||||
|
||||
@Test
|
||||
@DisplayName("Результат должен быть обернут")
|
||||
fun shouldBeWrappedResponse() {
|
||||
mockMvc
|
||||
.perform(MockMvcRequestBuilders.get("/wrapper/wrap"))
|
||||
.andDo(MockMvcResultHandlers.print())
|
||||
.andExpect(MockMvcResultMatchers.status().isOk)
|
||||
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(MockMvcResultMatchers.jsonPath("$.result.wrap", Matchers.`is`("yes")))
|
||||
.andExpect(MockMvcResultMatchers.jsonPath("$.errorCode", Matchers.`is`(0)))
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Результат НЕ должен быть обернут")
|
||||
fun shouldBeNoWrappedResponse() {
|
||||
mockMvc
|
||||
.perform(MockMvcRequestBuilders.get("/wrapper/no-wrap"))
|
||||
.andDo(MockMvcResultHandlers.print())
|
||||
.andExpect(MockMvcResultMatchers.status().isOk)
|
||||
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_VALUE))
|
||||
.andExpect(MockMvcResultMatchers.jsonPath("$.wrap", Matchers.`is`("no")))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -31,3 +31,4 @@ include("logger-spring-web")
|
|||
include("exception-handler-spring-web")
|
||||
include("exception-handler-logger-spring-web")
|
||||
include("version-spring-web")
|
||||
include("response-wrapper-spring-web")
|
||||
|
|
|
|||
Loading…
Reference in New Issue