Issue
I'm developing APIs for an exam project, but I wanted their responses to be consistently using a wrapping class on all of them (Telegram Bot API style for those who know them).
So, for example, having these two classes:
public class User {
public int id;
public String name;
}
public class Item {
public int id;
public String itemName;
public User owner;
}
What Spring returns to me is this output:
{
"id": 1,
"itemName": "theItem",
"owner": {
"id": 2,
"name": "theUser"
}
}
What I want instead is for this output to be returned:
{
"ok": true,
"data": {
"id": 1,
"itemName": "theItem",
"owner": {
"id": 2,
"name": "theUser"
}
}
}
Maybe using a class wrapper like this:
public class ResponseWrapper<T> {
public boolean ok;
public T data;
}
Is it possible to do this?
Solution
I thank @JustinMathew for the help, at the end, in my case (using Spring WebFlux with Kotlin), the ResponseBodyResultHandler class was more useful to me.
// 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
)
}
Edit: I made of this answer a library for Spring WebFlux 2.7.3 here.
P.S. I also took a cue from this other question, which faces the same problem but with Java.
Answered By - Giuseppe
Answer Checked By - Mildred Charles (JavaFixing Admin)