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.