title: How to realize custom exception handler using Spring WebFlux
body: <p>A have next classes:</p>
  <p>SaleRouter.java</p>
  <pre><code>@Configuration
  public class SaleRouter {
  
  @Bean
  public RouterFunction&lt;ServerResponse&gt; route(@Value(&quot;${server.servlet.context-path}&quot;) String contextPath,
                                              SaleHandler saleHandler) {
      return RouterFunctions.route().path(contextPath, mainPath -&gt; mainPath.path(&quot;/sale&quot;, funcPath -&gt; funcPath
              .POST(saleHandler::postSale)))
              .DELETE(saleHandler::deleteSale).filter(validate()).build();
  }
  
  private static HandlerFilterFunction&lt;ServerResponse, ServerResponse&gt; validate() {
      return (serverRequest, handlerFunction) -&gt; {
          Optional&lt;String&gt; email = serverRequest.queryParam(&quot;email&quot;);
          if (email.isEmpty()) {
              throw new BadRequestException(&quot;Error in input param: email - null&quot;);
          } else if (!EmailValidator.getInstance().isValid(email.get())) {
              throw new BadRequestException(&quot;Error in input param: email - &quot; + email.get());
          }
          return handlerFunction.handle(serverRequest);
      };
  }
  }
  </code></pre>
  <p>SaleHandler.java</p>
  <pre><code>@Component
  public class SaleHandler {
  public Mono&lt;ServerResponse&gt; postSale(ServerRequest request) {
      return ok().build();
  }
  
  public Mono&lt;ServerResponse&gt; deleteSale(ServerRequest request) {
      return ok().build();
  }
  }
  </code></pre>
  <p>RestExceptionHandler.java</p>
  <pre><code>@Component
  @Order(-2)
  @Slf4j
  public class RestExceptionHandler implements WebExceptionHandler {
  
  @Override
  public Mono&lt;Void&gt; handle(ServerWebExchange serverWebExchange, Throwable throwable) {
      serverWebExchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
      if (throwable instanceof BadRequestException) {
          serverWebExchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
          ErrorDetail errorDetail = new ErrorDetail(&quot;BadRequest&quot;, throwable.getMessage());
          return serverWebExchange.getResponse()
                  .writeWith(new Jackson2JsonEncoder()
                          .encode(Mono.just(errorDetail), serverWebExchange.getResponse().bufferFactory(),
                                  ResolvableType.forInstance(errorDetail), MediaType.APPLICATION_JSON,
                                  Hints.from(Hints.LOG_PREFIX_HINT, serverWebExchange.getLogPrefix())));
      }
      return Mono.empty();
  }
  }
  </code></pre>
  <p>BadRequestException.class</p>
  <pre><code>@Getter
  public class BadRequestException extends RuntimeException {
  private final String message;
  
  public BadRequestException(String message){
      this.message = message;
  }
  }
  </code></pre>
  <p>ErrorDetails.java</p>
  <pre><code>@NoArgsConstructor
  @AllArgsConstructor
  @Data
  public class ErrorDetail {
      private final String serviceName = &quot;app&quot;;
      private String errorCode;
      private String userMessage;
  }
  </code></pre>
  <p>SaleRouterTests.java</p>
  <pre><code>class SaleRouterTests {
  
  private static final String EMAIL = &quot;email&quot;;
  private static final String EMAIL_VALUE = &quot;test@gmail.com&quot;;
  
  private static final String SALE_PATH = &quot;/sale&quot;;
  private static final String SERVICE_NAME = &quot;app&quot;;
  private static final String BAD_REQUEST = &quot;BadRequest&quot;;
  
  private static final RouterFunction&lt;ServerResponse&gt; saleRouterFunc =
          new SaleRouter().route(&quot;&quot;, new SaleHandler());
  
  private final WebTestClient webTestClient;
  
  public SaleRouterTests() {
      webTestClient = WebTestClient.bindToRouterFunction(saleRouterFunc).build();
  }
  
  @Test
  void givenSaleRouter_WhenSalePostWithValidQueryParams_ThenOk() {
      webTestClient.post()
              .uri(uriBuilder -&gt;
                      uriBuilder.path(SALE_PATH)
                              .queryParam(EMAIL, EMAIL_VALUE)
                              .build())
              .exchange()
              .expectStatus().isOk();
  }
  
  @Test
  void givenSaleRouter_WhenSalePostWithoutEmailQueryParam_ThenBadRequest() {
      webTestClient.post()
              .uri(uriBuilder -&gt;
                      uriBuilder.path(SALE_PATH)
                              .build())
              .exchange()
              .expectStatus().isBadRequest()
              .expectBody(ErrorDetail.class)
              .value(response -&gt; {
                  Assertions.assertEquals(&quot;Error in input param: email - null&quot;, response.getUserMessage());
                      });
      }
    }
  }
  </code></pre>
  <p>I want validate query params and return error response using calling exception if param email in null.
  But this doesn't work.</p>
  <p>Output from test class:</p>
  <pre><code>com.pack.exception.BadRequestException: Error in input param: email - null
  at com.pack.controller.SaleRouter.lambda$validate$2(SaleRouter.java:40)
  Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
  Error has been observed at the following site(s):
  |_ checkpoint ⇢ HTTP POST &quot;/sale&quot; [ExceptionHandlingWebHandler]
  Stack trace:
      at com.pack.controller.SaleRouter.lambda$validate$2(SaleRouter.java:40)
      at org.springframework.web.reactive.function.server.HandlerFilterFunction.lambda$apply$2(HandlerFilterFunction.java:72)
      at org.springframework.web.reactive.function.server.RouterFunctions$RouterFunctionWebHandler.lambda$null$1(RouterFunctions.java:1253)
      at org.springframework.web.reactive.function.server.RouterFunctions$RouterFunctionWebHandler.wrapException(RouterFunctions.java:1271)
      at org.springframework.web.reactive.function.server.RouterFunctions$RouterFunctionWebHandler.lambda$null$2(RouterFunctions.java:1253)
      at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125)
      at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:99)
      at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:120)
      at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)
      at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:281)
  
  java.lang.AssertionError: Status expected:&lt;400 BAD_REQUEST&gt; but was:&lt;500 INTERNAL_SERVER_ERROR&gt;
  
  &gt; POST /sale
  &gt; WebTestClient-Request-Id: [1]
  
  No content
  
  &lt; 500 INTERNAL_SERVER_ERROR Internal Server Error
  &lt; 
  
  0 bytes of content (unknown content-type).
  </code></pre>
  <p>tell me please how to do it correctly and where my mistake?
  There is a suspicion that RestExceptionHandler does not work, but why?</p>