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?
CodePudding user response:
I understand you need a global setting to convert all your responses into a standard one. For this you can implement ResponseBodyAdvice and have a common structure for all your api responses. Refer this link for a detailed example
CodePudding user response:
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: Int,
val message: String?
)
}
// File: /wrapper/util/PublisherExtensions.kt
fun <T> Mono<T>.toServiceResponse(): Mono<Response<T>> =
this.map { r -> Response(true, r, null) }
.onErrorResume { e -> Mono.just(Response(false, null, Response.Error(500, e.message))) }
fun <T> Flux<T>.toServiceResponse(): Mono<Response<List<T>>> =
this.collectList()
.map { r -> Response(true, r, null) }
.onErrorResume { e -> Mono.just(Response(false, null, Response.Error(500, e.message))) }
// 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)
override fun handleResult(exchange: ServerWebExchange, result: HandlerResult): Mono<Void> {
val body = when (val value = result.returnValue) {
is Mono<*> -> value.toServiceResponse()
is Flux<*> -> value.toServiceResponse()
else -> throw RuntimeException("The \"body\" should be Mono<*> or Flux<*>!")
}
return writeBody(body, result.returnTypeSource, exchange)
}
P.S. I also took a cue from this other question, which faces the same problem but with Java.