I'd like to strip the reactive DefaultErrorAttributes to only expose timestamp and message fields in the json response.
Especially, I want to get rid of the status field in the json response, as this is already clear from the http response header.
While https://github.com/spring-projects/spring-boot/issues/30011 is marked as fixed (a NPE is not thrown anymore), it does not help. Because WebTestClient assertions still rely on the existence of a status field inside the json response (instead it should rely on the HTTP status header response).
@Component
public class ReactiveHidingDefaultErrorAttributes extends org.springframework.boot.web.reactive.error.DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
Map<String, Object> map = super.getErrorAttributes(request, options);
map.keySet().retainAll(Set.of("timestamp", "message"));
return map;
}
}
@RestController
public class ExampleServlet {
@GetMapping("/test")
public Mono<String> greeting(ServerHttpRequest request) {
throw new ResponseStatusException(HttpStatus.I_AM_A_TEAPOT, "junit");
}
}
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
@AutoConfigureWebTestClient
public class ReactiveDefaultExceptionHandlerTest
@Test
public void test() {
webTestClient.get().uri("/test")
.exchange()
.expectStatus().isEqualTo(HttpStatus.I_AM_A_TEAPOT);
}
}
The test fails with:
2024-08-12T14:47:13.735+02:00 ERROR 72375 --- [ parallel-2] o.s.w.s.a.HttpWebHandlerAdapter : [7292913d] 500 Server Error for HTTP GET "/status"
java.lang.IllegalStateException: ErrorAttributes must contain a status integer
at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-6.1.11.jar:6.1.11]
at org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler.getHttpStatus(DefaultErrorWebExceptionHandler.java:244) ~[spring-boot-autoconfigure-3.3.2.jar:3.3.2]
at org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler.renderErrorResponse(DefaultErrorWebExceptionHandler.java:151) ~[spring-boot-autoconfigure-3.3.2.jar:3.3.2]
at org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler.lambda$handle$0(AbstractErrorWebExceptionHandler.java:299) ~[spring-boot-autoconfigure-3.3.2.jar:3.3.2]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:132) [reactor-core-3.6.8.jar:3.6.8]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) [reactor-core-3.6.8.jar:3.6.8]
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) [reactor-core-3.6.8.jar:3.6.8]
at reactor.core.publisher.FluxConcatArray$ConcatArraySubscriber.onNext(FluxConcatArray.java:180) [reactor-core-3.6.8.jar:3.6.8]
Comment From: philwebb
I think we had some trouble directly getting the status from the DefaultErrorWebExceptionHandler. Rather than using a custom DefaultErrorAttributes bean, are you able to plug in your own DefaultErrorWebExceptionHandler and override the getErrorAttributeOptions method to exclude STATUS?
Comment From: membersound
Even if so, that would be quite error-prone, as I'd have to replace the @Bean definition from ErrorWebFluxAutoConfiguration.
Which in turn means, for every spring-boot release I'd have to check if the bean definition changes from current:
public class ErrorWebFluxAutoConfiguration {
@Bean
@ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class, search = SearchStrategy.CURRENT)
@Order(-1)
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes,
WebProperties webProperties, ObjectProvider<ViewResolver> viewResolvers,
ServerCodecConfigurer serverCodecConfigurer, ApplicationContext applicationContext) {
DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(errorAttributes,
webProperties.getResources(), this.serverProperties.getError(), applicationContext);
exceptionHandler.setViewResolvers(viewResolvers.orderedStream().toList());
exceptionHandler.setMessageWriters(serverCodecConfigurer.getWriters());
exceptionHandler.setMessageReaders(serverCodecConfigurer.getReaders());
return exceptionHandler;
}
}
If there would be some kind of Configurer similar to WebFluxConfigurer that could be used to modify the bean creation, then I could imagine the approach might work. But not as is.
Comment From: philwebb
@membersound I think #41732 should have fixed this.