(前提:问题就在最后)
我使用Sping Boot 2.7.4创建了一个新项目,它使用了Spring Web。
我必须为Json对象使用一个定制库,所以我为表示这些Json对象的类创建了一个定制的HttpMessageConverter
。
我在Spring配置类中添加了该转换器,但是当我运行测试时,我使用的RestTemplate
似乎由于某种原因没有使用它。
我得到的错误是这样的:
org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [interface com.example.MyJsonClass] and content type [application/json;charset=UTF-8]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:126)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1037)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1020)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:778)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711)
at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:468)
at org.springframework.boot.test.web.client.TestRestTemplate.postForEntity(TestRestTemplate.java:440)
at com.example.Tests.test(Tests.java:61)
在给你看代码之前,我会提到我找到了一种方法来让它工作,但我不明白为什么需要它。我会在代码之后发布它。
下面是代码:
配置方式:
@SpringBootApplication
public class MyApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
// adding the custom converter here
messageConverters.add(new MyCustomHttpMessageConverter());
}
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
自定义转换器(不能透露实现...):
public class MyCustomHttpMessageConverter implements HttpMessageConverter<MyJsonClass> {
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
// implementation...
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
// implementation...
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return List.of(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8);
}
@Override
public MyJsonClass read(Class<? extends MyJsonClass> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
// implementation...
}
@Override
public void write(MyJsonClass t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// implementation...
}
}
控制器:
@Controller
@RequestMapping("/")
public class MyController {
@Autowired
private RestTemplate restTemplate;
@PostMapping(path = "/test", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<MyJsonClass> test(@RequestBody MyJsonClass body) {
System.out.println("test endpoint called.");
// makes a remote call to an external service
ResponseEntity<MyJsonClass> response = restTemplate.postForEntity(
"http://localhost:8080/test",// this is normally a remote endpoint, I actually set it to localhost in the test configuration properties
body, MyJsonClass.class);
return ResponseEntity.ok(response.getBody());
}
}
测试类别:
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class Tests {
@Autowired
private TestRestTemplate testRestTemplate;
@Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockServer;
private ObjectMapper mapper = new ObjectMapper();
@BeforeEach
public void init() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
@Test
public void test() throws Exception {
MyJsonClass testPayload = new MyJsonClass("testProperty", "testValue");// just an example
// I need to fake a request to a remote server
mockServer.expect(ExpectedCount.once(),
requestTo(new URI("http:localhost:8080/test")))
.andExpect(method(HttpMethod.POST))
.andExpect(content().string("{\"testProperty\":\"testValue\"}"))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(mapper.writeValueAsString(testPayload.clone().set("testProperty2", "testValue2"))));
ResponseEntity<MyJsonClass> response = testRestTemplate.postForEntity("/test", testPayload, MyJsonClass.class);
// assertions here...
}
}
我让它工作的方法是显式地将转换器添加到2 RestTemplate
中。但我认为不需要它,因为我将它添加到了配置类的configureMessageConverters
方法中。restTemplate
Bean的创建更改为:
@Bean
RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MyCustomHttpMessageConverter());
return restTemplate;
}
在测试类中,在@BeforeEach
中,我为TestRestTemplate
添加了以下行:
testRestTemplate.getRestTemplate().getMessageConverters().add(new MyCustomHttpMessageConverter());
所以我猜最后一个问题是:如果我已经通过WebMvcConfigurer
类的实现在配置中添加了转换器,为什么还必须显式地将其添加到每个RestTemplate
中呢?(注意:我也尝试过使用extendMessageConverters
方法,结果相同)。
或者:为什么RestTemplate
没有使用我在配置中添加的转换器?
1条答案
按热度按时间uajslkp61#
首先,手动创建RestTemplate,而不使用Springbean注入。这样,Spring就不能自动连接任何配置。
您应该改用RestTemplateBuilder来使用Spring注入。
然后,由于您使用的是Sping Boot ,因此不必通过WebMvcConfigurer注册转换器,而只需使用HttpMethodConverter注册bean即可
这样,Spring应该首先使用HttpMessageConverter创建一个bean,然后自动将其注入到RestTemplateBuilder中。