I mock a WebClient
response with response header MediaType.APPLICATION_JSON_VALUE
, and statuscode 400
.
When I run the test with WebTestClient
, the returned statuscode is wrong:
@SpringBootTest
@AutoConfigureMockMvc
@Import(WebfluxWebClientServletTest.TestController.class)
public class WebfluxWebClientServletTest {
@Autowired
private WebTestClient webTestClient;
@Autowired
private ObjectMapper mapper;
@Test
public void test() throws Exception {
MockWebServer server = new MockWebServer();
server.start(8090);
MockResponse mock = new MockResponse();
mock.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
mock.setResponseCode(400);
mock.setBody(mapper.writeValueAsString(Map.of("junit", "test")));
server.enqueue(mock);
webTestClient.get().uri("/junit")
.exchange()
.expectStatus().isBadRequest()
.expectHeader()
.contentTypeCompatibleWith(MediaType.APPLICATION_JSON); //assertion fails
}
@RestController
static class TestController {
@Autowired
private WebClient client;
@GetMapping(value = "/junit", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<Object> send() {
return client.get().uri("localhost:8090/external")
.retrieve()
.bodyToMono(Object.class);
}
}
}
The test fails with:
2022-03-10 17:14:23,939 [][] ERROR [main] o.s.t.w.r.s.ExchangeResult : Request details for assertion failure:
> GET /junit
> WebTestClient-Request-Id: [1]
No content
< 400 BAD_REQUEST Bad Request
< X-Request-ID: [OqBTNj]
< Content-Type: [text/plain;charset=UTF-8]
< Content-Length: [16]
< Cache-Control: [no-cache, no-store, max-age=0, must-revalidate]
< Pragma: [no-cache]
< Expires: [0]
< X-Content-Type-Options: [nosniff]
< X-Frame-Options: [DENY]
< X-XSS-Protection: [1 ; mode=block]
< Referrer-Policy: [no-referrer]
{"junit":"test"}
java.lang.AssertionError: Response header 'Content-Type'=[text/plain;charset=UTF-8] is not compatible with [application/json]
Comment From: wilkinsona
It's not clear what you're trying to do here. You've mentioned WebFlux but you're using @AutoConfigureMockMvc
. Perhaps you're trying to use the MVC-backed WebTestClient
but that's not clear from what you've described. It also isn't clear which version of Spring Boot you're using.
If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.
Comment From: membersound
I added a zip for reproducing to the issue.
Sidenote: you're correct, I accidentally added @AutoConfigureMockMvc
, but @AutoConfigureWebTestClient
causes the same failure.
Comment From: bclozel
When using WebClient
, retrieve()
should emit an error message for an 4xx response status. I guess the problem might be in your controller implementation?
Comment From: membersound
Well, my goal is to have some kind of "global interceptor" that forwards the external error (received through webclient) both in statuscode + body. I want each WebClient
call in my application to automatically apply this interceptor.
So is using a @RestControllerAdvice
the wrong approach for this?
Comment From: bclozel
You've created this issue to report a bug in Spring Boot - it doesn't seem that's the case.
The behavior you're describing here happens because the ResponseEntity
returned by the exception handler doesn't declare any content type. Changing it to the following works:
@ExceptionHandler(WebClientResponseException.class)
public ResponseEntity<Object> webClientResponseHandler(WebClientResponseException e) {
return ResponseEntity.status(e.getStatusCode()).contentType(MediaType.APPLICATION_JSON).body(e.getResponseBodyAsString());
}
If you have questions, please use StackOverflow.
Comment From: membersound
Okay, I see that github issues are not for implementation discussions. I will move to SO.
Anyways, you're correct: changing the handler as follows resolves the problem:
return ResponseEntity.status(e.getStatusCode())
.contentType(e.getHeaders().getContentType())
.body(e.getResponseBodyAsString());