(前提:問題在最后)
我使用 Spring Boot 2.7.4 創建了一個使用 Spring Web 的新專案。
我必須為 Json 物件使用自定義庫,因此我HttpMessageConverter為表示這些 Json 物件的類進行了自定義。
我在 Spring 配置類中添加了該轉換器,但是當我運行測驗時,RestTemplate我使用的 s 似乎由于某種原因沒有使用它。
我得到的錯誤是這樣的:
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 RestTemplates。但是我認為不需要它,因為我configureMessageConverters在配置類的方法中添加了它。
bean的創建restTemplate變成了這樣:
@Bean
RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MyCustomHttpMessageConverter());
return restTemplate;
}
在測驗類中,@BeforeEach我添加了這一行TestRestTemplate:
testRestTemplate.getRestTemplate().getMessageConverters().add(new MyCustomHttpMessageConverter());
RestTemplate所以我想最后一個問題是:如果我通過類的實作將它添加到配置中,為什么我必須將轉換器顯式添加到每個WebMvcConfigurer?(注意:我也嘗試過使用該extendMessageConverters方法,結果相同)。
或者:為什么RestTemplates 不使用我在配置中添加的轉換器?
uj5u.com熱心網友回復:
首先,您手動創建 RestTemplate,而不使用 Spring bean 注入。這樣,Spring 無法自動裝配任何配置。
您應該使用 RestTemplateBuilder 來使用 Spring 注入。
@Bean
RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
然后,當您使用 Spring 啟動時,您不必通過 WebMvcConfigurer 注冊轉換器,但您可以簡單地使用 HttpMethodConverter 注冊一個 bean
@Configuration
public class SpringConfiguration{
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
@Bean
public HttpMessageConverter<MyJsonClass> myCustomHttpMessageConverter() {
return new MyCustomHttpMessageConverter();
}
}
這樣,Spring 應該首先使用您的 HttpMessageConverter 創建一個 bean,然后它應該自動將其注入 RestTemplateBuilder。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/520516.html
下一篇:聊一聊分布式鎖的設計模型
