Spring WebFlux - 在序列化之前添加包装 class
Spring WebFlux - Add a wrapping class before serialization
我正在为一个考试项目开发 APIs,但我希望他们的回答始终如一地使用包装 class 对所有这些(Telegram Bot API 样式认识他们的人)。
所以,例如,有这两个 classes:
public class User {
public int id;
public String name;
}
public class Item {
public int id;
public String itemName;
public User owner;
}
对我来说 Spring returns 是这个输出:
{
"id": 1,
"itemName": "theItem",
"owner": {
"id": 2,
"name": "theUser"
}
}
我想要的是返回此输出:
{
"ok": true,
"data": {
"id": 1,
"itemName": "theItem",
"owner": {
"id": 2,
"name": "theUser"
}
}
}
也许使用这样的 class 包装器:
public class ResponseWrapper<T> {
public boolean ok;
public T data;
}
可以这样做吗?
我知道您需要一个全局设置来将您的所有回复转换为标准回复。为此,您可以实施 ResponseBodyAdvice and have a common structure for all your api responses. Refer this link 以获得详细示例
编辑:对于 spring-webflux,您可以扩展 ResponseBodyResultHandler
并覆盖 handleResult
。 answer
中给出了一个示例
感谢@JustinMathew 的帮助,最后,就我而言(使用 Spring WebFlux 和 Kotlin),ResponseBodyResultHandler class 对我更有用。
// File: /MicroserviceApplication.kt
@SpringBootApplication
class MicroserviceApplication {
@Autowired
lateinit var serverCodecConfigurer: ServerCodecConfigurer
@Autowired
lateinit var requestedContentTypeResolver: RequestedContentTypeResolver
@Bean
fun responseWrapper(): ResponseWrapper = ResponseWrapper(
serverCodecConfigurer.writers, requestedContentTypeResolver
)
}
// File: /wrapper/model/Response.kt
data class Response<T>(
val ok: Boolean,
val data: T?,
val error: Error? = null
) {
data class Error(
val value: HttpStatus,
val message: String?
)
}
// File: /wrapper/ResponseWrapper.kt
class ResponseWrapper(writers: List<HttpMessageWriter<*>>, resolver: RequestedContentTypeResolver) :
ResponseBodyResultHandler(writers, resolver) {
override fun supports(result: HandlerResult): Boolean =
(result.returnType.resolve() == Mono::class.java)
|| (result.returnType.resolve() == Flux::class.java)
@Throws(ClassCastException::class)
override fun handleResult(exchange: ServerWebExchange, result: HandlerResult): Mono<Void> {
val body = when (val value = result.returnValue) {
is Mono<*> -> value
is Flux<*> -> value.collectList()
else -> throw ClassCastException("The \"body\" should be Mono<*> or Flux<*>!")
}
.map { r -> Response(true, r, null) }
.onErrorMap { e ->
if (e !is Response.Error)
Response.Error(HttpStatus.INTERNAL_SERVER_ERROR, "Internal Server Error")
else e
}
.onErrorResume { e -> Mono.just(Response(false, null, e as Response.Error)) }
return writeBody(body, returnType, exchange)
}
companion object {
@JvmStatic
private fun methodForReturnType(): Mono<Response<Any>>? = null
private val returnType: MethodParameter = MethodParameter(
ResponseWrapper::class.java.getDeclaredMethod("methodForReturnType"), -1
)
}
P.S。我也从另一个 那里得到了启发,它面临同样的问题,但 Java.
我正在为一个考试项目开发 APIs,但我希望他们的回答始终如一地使用包装 class 对所有这些(Telegram Bot API 样式认识他们的人)。
所以,例如,有这两个 classes:
public class User {
public int id;
public String name;
}
public class Item {
public int id;
public String itemName;
public User owner;
}
对我来说 Spring returns 是这个输出:
{
"id": 1,
"itemName": "theItem",
"owner": {
"id": 2,
"name": "theUser"
}
}
我想要的是返回此输出:
{
"ok": true,
"data": {
"id": 1,
"itemName": "theItem",
"owner": {
"id": 2,
"name": "theUser"
}
}
}
也许使用这样的 class 包装器:
public class ResponseWrapper<T> {
public boolean ok;
public T data;
}
可以这样做吗?
我知道您需要一个全局设置来将您的所有回复转换为标准回复。为此,您可以实施 ResponseBodyAdvice and have a common structure for all your api responses. Refer this link 以获得详细示例
编辑:对于 spring-webflux,您可以扩展 ResponseBodyResultHandler
并覆盖 handleResult
。
感谢@JustinMathew 的帮助,最后,就我而言(使用 Spring WebFlux 和 Kotlin),ResponseBodyResultHandler class 对我更有用。
// File: /MicroserviceApplication.kt
@SpringBootApplication
class MicroserviceApplication {
@Autowired
lateinit var serverCodecConfigurer: ServerCodecConfigurer
@Autowired
lateinit var requestedContentTypeResolver: RequestedContentTypeResolver
@Bean
fun responseWrapper(): ResponseWrapper = ResponseWrapper(
serverCodecConfigurer.writers, requestedContentTypeResolver
)
}
// File: /wrapper/model/Response.kt
data class Response<T>(
val ok: Boolean,
val data: T?,
val error: Error? = null
) {
data class Error(
val value: HttpStatus,
val message: String?
)
}
// File: /wrapper/ResponseWrapper.kt
class ResponseWrapper(writers: List<HttpMessageWriter<*>>, resolver: RequestedContentTypeResolver) :
ResponseBodyResultHandler(writers, resolver) {
override fun supports(result: HandlerResult): Boolean =
(result.returnType.resolve() == Mono::class.java)
|| (result.returnType.resolve() == Flux::class.java)
@Throws(ClassCastException::class)
override fun handleResult(exchange: ServerWebExchange, result: HandlerResult): Mono<Void> {
val body = when (val value = result.returnValue) {
is Mono<*> -> value
is Flux<*> -> value.collectList()
else -> throw ClassCastException("The \"body\" should be Mono<*> or Flux<*>!")
}
.map { r -> Response(true, r, null) }
.onErrorMap { e ->
if (e !is Response.Error)
Response.Error(HttpStatus.INTERNAL_SERVER_ERROR, "Internal Server Error")
else e
}
.onErrorResume { e -> Mono.just(Response(false, null, e as Response.Error)) }
return writeBody(body, returnType, exchange)
}
companion object {
@JvmStatic
private fun methodForReturnType(): Mono<Response<Any>>? = null
private val returnType: MethodParameter = MethodParameter(
ResponseWrapper::class.java.getDeclaredMethod("methodForReturnType"), -1
)
}
P.S。我也从另一个