Creating custom ErrorWebExceptionHandler fails
Asked Answered
B

5

19

I am trying to create my own ErrorWebExceptionHandler in Spring Boot 2 by extending the default one but my application fails to start with the following message:

Caused by: java.lang.IllegalArgumentException: Property 'messageWriters' is required
at org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler.afterPropertiesSet(AbstractErrorWebExceptionHandler.java:214) ~[spring-boot-autoconfigure-2.0.4.RELEASE.jar:2.0.4.RELEASE]

My handler (Kotlin code):

@Component
@Order(-2)
class SampleErrorWebExceptionHandler(
    errorAttributes: ErrorAttributes?,
    resourceProperties: ResourceProperties?,
    errorProperties: ErrorProperties?,
    applicationContext: ApplicationContext?
) : DefaultErrorWebExceptionHandler(errorAttributes, resourceProperties, errorProperties, applicationContext) {

    override fun logError(request: ServerRequest, errorStatus: HttpStatus) {
        // do something
    }
}

What could be the cause?

Brickwork answered 25/9, 2018 at 12:8 Comment(0)
O
11

You need to set the messageWriters on that instance, because they are required here. You should probably create that as a @Bean, just like Spring Boot is doing in the dedicated auto-configuration.

Ocreate answered 26/9, 2018 at 1:44 Comment(2)
I have added a dependency on ServerCodecConfigurer from where I can get messageWriters and messageReaders and it is working now, thanks!Brickwork
hello @MiloszTylenda can you provide an answer here, I also facing the same problem and the solution is not very clearAnteroom
F
35

Please try to add dependency of ServerCodecConfigurer in your constructor

GlobalErrorWebExceptionHandler(
  ErrorAttributes errorAttributes,
  ResourceProperties resourceProperties,
  ApplicationContext applicationContext,
  ServerCodecConfigurer configurer
) {
    super(errorAttributes, resourceProperties, applicationContext);
    this.setMessageWriters(configurer.getWriters());
}
Fissi answered 27/5, 2019 at 4:55 Comment(1)
Cannot resolve symbol 'ResourceProperties'Galitea
O
11

You need to set the messageWriters on that instance, because they are required here. You should probably create that as a @Bean, just like Spring Boot is doing in the dedicated auto-configuration.

Ocreate answered 26/9, 2018 at 1:44 Comment(2)
I have added a dependency on ServerCodecConfigurer from where I can get messageWriters and messageReaders and it is working now, thanks!Brickwork
hello @MiloszTylenda can you provide an answer here, I also facing the same problem and the solution is not very clearAnteroom
A
3

I just did this as well, and after looking at springs implementation I just added the components to the constructor.

@Component
@Order(-2)
class GlobalErrorWebExceptionHandler(
        errorAttributes: ErrorAttributes,
        resourceProperties: ResourceProperties,
        applicationContext: ApplicationContext,
        viewResolvers: ObjectProvider<ViewResolver>,
        serverCodecConfigurer: ServerCodecConfigurer
) : AbstractErrorWebExceptionHandler(
        errorAttributes,
        resourceProperties,
        applicationContext
) {
    private val logger = LogFactory.getLog(GlobalErrorWebExceptionHandler::class.java)!!

    init {
        setViewResolvers(viewResolvers.orderedStream().collect(Collectors.toList()))
        setMessageWriters(serverCodecConfigurer.writers)
        setMessageReaders(serverCodecConfigurer.readers)
    }

    override fun getRoutingFunction(errorAttributes: ErrorAttributes) = RouterFunctions.route(RequestPredicates.all(), HandlerFunction { request ->
        val ex = getError(request)
        logger.error(ex.message)

        ServerResponse.ok().build()
    })
}
Ascent answered 28/4, 2020 at 16:33 Comment(0)
I
3

If the above solution is not very clear refer below:

@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    private final ObjectProvider<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;


    public GlobalErrorWebExceptionHandler(ObjectProvider<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer,
                                          ErrorAttributes errorAttributes, WebProperties.Resources resources, ApplicationContext applicationContext) {
        super(errorAttributes, resources, applicationContext);
        this.viewResolvers = viewResolvers;
        this.serverCodecConfigurer = serverCodecConfigurer;
        super.setMessageWriters(serverCodecConfigurer.getWriters());
        super.setMessageReaders(serverCodecConfigurer.getReaders());
        super.setViewResolvers(viewResolvers.orderedStream().collect(Collectors.toList()));
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    private Mono<ServerResponse> renderErrorResponse(ServerRequest serverRequest) {
        Map<String, Object> errorPropertiesMap = getErrorAttributes(serverRequest,
                ErrorAttributeOptions.defaults());

        return ServerResponse.status(HttpStatus.BAD_REQUEST)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(errorPropertiesMap));
    }
}
Impressment answered 9/12, 2021 at 7:3 Comment(0)
B
1

I had the same issue, despite I was implementing the way from Spring documentation, but I figured it out after I was your solutions and guessing how it should in Kotlin.

in case someone might be interested in the future, this is how it should be.

@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
            )
    }
    
}
Bite answered 31/8, 2023 at 18:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.