Dear Spring Team, Hope that you are doing very well. I was reading your documentation to learn about exception handling in Reactive Web, and I tried to apply the example code that you provided in Kotlin but i faced several problems
https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/web.html#web.reactive.webflux.error-handling
I worked on my own to make it work, at the end I found out how to do it, and I thought it would be a good idea if you would like to update the code.
package com.saher.testing.exception
import org.springframework.boot.autoconfigure.web.WebProperties
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler
import org.springframework.boot.web.reactive.error.ErrorAttributes
import org.springframework.context.ApplicationContext
import org.springframework.http.HttpStatus
import org.springframework.http.codec.ServerCodecConfigurer
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.*
@Component
class GlobalErrorWebExceptionHandler(
errorAttributes: ErrorAttributes,
resourceProperties: WebProperties.Resources,
applicationContext: ApplicationContext,
serverCodecConfigurer: ServerCodecConfigurer
) : AbstractErrorWebExceptionHandler(
errorAttributes,
resourceProperties,
applicationContext
) {
init {
super.setMessageWriters(serverCodecConfigurer.writers)
super.setMessageReaders(serverCodecConfigurer.readers)
}
override fun getRoutingFunction(errorAttributes: ErrorAttributes): RouterFunction<ServerResponse> {
return coRouter {
GET("/exception", ::exceptionHandler)
}
}
private suspend fun exceptionHandler(serverRequest: ServerRequest): ServerResponse {
return ServerResponse
.status(HttpStatus.BAD_REQUEST)
.bodyValueAndAwait(
getError(serverRequest).localizedMessage
)
}
}
also you need to add this bean in the configuration
import org.springframework.boot.autoconfigure.web.WebProperties
import org.springframework.context.annotation.Bean
import org.springframework.stereotype.Component
@Component
class Resources {
@Bean
fun webResources(): WebProperties.Resources {
return WebProperties.Resources()
}
}
I created a stackoverflow post for it over here
https://stackoverflow.com/questions/77383331/spring-webflux-reactive-error-handling-kotlin
Comment From: mhalbritter
Thanks for letting us know. However, I think the @Bean method in your fix isn't the right way.
Here's the Java Code that's working:
@Component
public class MyErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, WebProperties webProperties,
ApplicationContext applicationContext, ServerCodecConfigurer serverCodecConfigurer) {
super(errorAttributes, webProperties.getResources(), applicationContext);
setMessageReaders(serverCodecConfigurer.getReaders());
setMessageWriters(serverCodecConfigurer.getWriters());
}
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml);
}
private boolean acceptsXml(ServerRequest request) {
return request.headers().accept().contains(MediaType.APPLICATION_XML);
}
public Mono<ServerResponse> handleErrorAsXml(ServerRequest request) {
ServerResponse.BodyBuilder builder = ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR);
// ... additional builder calls
return builder.build();
}
}
Comment From: mhalbritter
That would be the Kotlin equivalent:
@Component
class MyErrorWebExceptionHandler(
errorAttributes: ErrorAttributes, webProperties: WebProperties,
applicationContext: ApplicationContext, serverCodecConfigurer: ServerCodecConfigurer
) : AbstractErrorWebExceptionHandler(errorAttributes, webProperties.resources, applicationContext) {
init {
setMessageReaders(serverCodecConfigurer.readers)
setMessageWriters(serverCodecConfigurer.writers)
}
override fun getRoutingFunction(errorAttributes: ErrorAttributes): RouterFunction<ServerResponse> {
return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml)
}
private fun acceptsXml(request: ServerRequest): Boolean {
return request.headers().accept().contains(MediaType.APPLICATION_XML)
}
fun handleErrorAsXml(request: ServerRequest?): Mono<ServerResponse> {
val builder = ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
// ... additional builder calls
return builder.build()
}
}