WebExceptionHandler : How to write a body with Spring Webflux

后端 未结 4 1419
情书的邮戳
情书的邮戳 2021-02-19 21:23

I want to handle the Exception of my api by adding a WebExceptionHandler. I can change the status code, but I am stuck when i want to change the body of the response : ex adding

相关标签:
4条回答
  • 2021-02-19 22:04

    Given the answer, to serialize object I use this way :

     Mono<DataBuffer> db = commonsException.getErrorsResponse().map(errorsResponse -> {
    
         ObjectMapper objectMapper = new ObjectMapper();
         try {
             return objectMapper.writeValueAsBytes(errorsResponse);
         } catch (JsonProcessingException e) {
              return e.getMessage().getBytes();
         }
    }).map(s -> exchange.getResponse().bufferFactory().wrap(s));
    
    exchange.getResponse().getHeaders().add("Content-Type", "application/json");
    exchange.getResponse().setStatusCode(commonsException.getHttpStatus());
    return exchange.getResponse().writeWith(db);
    
    0 讨论(0)
  • 2021-02-19 22:04

    for anyone looking at a way to write JSON response body, here's a Kotlin code sample:

    fun writeBodyJson(body: Any, exchange: ServerWebExchange) =
        exchange.response.writeWith(
            Jackson2JsonEncoder().encode(
                Mono.just(body),
                exchange.response.bufferFactory(),
                ResolvableType.forInstance(body),
                MediaType.APPLICATION_JSON_UTF8,
                Hints.from(Hints.LOG_PREFIX_HINT, exchange.logPrefix))
        )
    

    not 100% sure that's the way to go though, would like to get some opinions.

    0 讨论(0)
  • 2021-02-19 22:07

    ServerResponse has a method writeTo which can be used to write your body to ServerExchange (Spring framework does it this way). Only problem is that you have to provide Contextas a second parameter, so I have just copied HandlerStrategiesResponseContext from framework implementation.

    Make sure that you are using at least Spring Boot 2.0.0 M2, before this version WebExceptionHandler was not registered while using RouterFunctions.

    import org.springframework.http.HttpStatus
    import org.springframework.http.HttpStatus.*
    import org.springframework.http.codec.HttpMessageWriter
    import org.springframework.stereotype.Component
    import org.springframework.web.reactive.function.server.HandlerStrategies
    import org.springframework.web.reactive.function.server.ServerResponse
    import org.springframework.web.reactive.result.view.ViewResolver
    import org.springframework.web.server.ServerWebExchange
    import org.springframework.web.server.WebExceptionHandler
    
    
    @Component
    class GlobalErrorHandler() : WebExceptionHandler {
    
        override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> =
            handle(ex)
                    .flatMap {
                        it.writeTo(exchange, HandlerStrategiesResponseContext(HandlerStrategies.withDefaults()))
                    }
                    .flatMap {
                        Mono.empty<Void>()
                    }
    
        fun handle(throwable: Throwable): Mono<ServerResponse> {
    
            return when (throwable) {
                is EntityNotFoundException -> {
                    createResponse(NOT_FOUND, "NOT_FOUND", "Entity not found, details: ${throwable.message}")
                }
                else -> {
                    createResponse(INTERNAL_SERVER_ERROR, "GENERIC_ERROR", "Unhandled exception")
                }
            }
        }
    
        fun createResponse(httpStatus: HttpStatus, code: String, message: String): Mono<ServerResponse> =
            ServerResponse.status(httpStatus).syncBody(ApiError(code, message))
    }
    
    private class HandlerStrategiesResponseContext(val strategies: HandlerStrategies) : ServerResponse.Context {
    
        override fun messageWriters(): List<HttpMessageWriter<*>> {
            return this.strategies.messageWriters()
        }
    
        override fun viewResolvers(): List<ViewResolver> {
            return this.strategies.viewResolvers()
        }
    }
    
    0 讨论(0)
  • 2021-02-19 22:15

    WebExceptionHandler is rather low level, so you have to directly deal with the request/response exchange.

    Note that:

    • the Mono<Void> return type should signal the end of the response handling; this is why it should be connected to the Publisher writing the response
    • at this level, you're dealing directly with data buffers (no serialization support available)

    Your WebExceptionHandler could look like this:

    (serverWebExchange, exception) -> {
    
      exchange.getResponse().setStatusCode(myStatusGivenTheException);
      byte[] bytes = "Some text".getBytes(StandardCharsets.UTF_8);
      DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
      return exchange.getResponse().writeWith(Flux.just(buffer));
    }
    
    0 讨论(0)
提交回复
热议问题