Spring Boot微服务调用出现No converter found错误排查求助
问题背景
开发了两个Spring Boot微服务:Service-one和Service-two,其中Service-one通过WebClient调用Service-two的接口获取数据。调用Service-one的/products接口时,抛出No converter found for return value of type: class java.util.HashMap错误。已使用@RestController(无需额外@ResponseBody),且已添加Jackson依赖,问题仍未解决。
相关代码
Service-one Controller
@Autowired WebClient client; HashMap<String,Object> finalList = new HashMap<String, Object>(); HashMap<Integer, String> productMap = new HashMap<Integer, String>(); @PostMapping(value = "/products/{id}") public void saveData(@PathVariable("id") Integer id, @RequestBody String message) { productMap.put(id, message); } @GetMapping(value = "/products") public HashMap<String, Object> getMessage() { Map<String, Object> details = client.get().uri("/details").retrieve().bodyToFlux(new ParameterizedTypeReference<Map<String,Object>>(){}) .blockFirst(); finalList.put("details", details); return finalList; }
Service-two Controller
@RestController @RequestMapping(value = "/details-service") public class DetailsServiceController { @GetMapping(value = "/details") private HashMap<String, Object> getDetails() { HashMap<String, Object> additionalDetails = new HashMap<String, Object>(); ArrayList<String> array = new ArrayList<String>(); additionalDetails.put("version", 1); array.add("ABC.com"); additionalDetails.put("vendors", array); return additionalDetails; } }
错误日志
org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class java.util.HashMap at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:230) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:181) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:123) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.33.jar:9.0.33] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.33.jar:9.0.33] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.33.jar:9.0.33] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) [tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1594) [tomcat-embed-core-9.0.33.jar:9.0.33] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.33.jar:9.0.33] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_251] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_251] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.33.jar:9.0.33] at java.lang.Thread.run(Thread.java:748) [na:1.8.0_251]
解决方案建议
1. 修正Service-two接口方法的访问权限
Service-two中getDetails()方法是private修饰的,Spring无法识别并映射这个接口方法,导致Service-one调用时返回异常数据。将其改为public:
@GetMapping(value = "/details") public HashMap<String, Object> getDetails() { // 原有逻辑不变 }
2. 修正WebClient的请求路径
Service-two的接口路径是/details-service/details,但Service-one中WebClient调用的是/details,路径不匹配导致调用失败。需配置WebClient的baseUrl,或者修改请求uri:
// 方式一:配置WebClient Bean时指定baseUrl @Bean public WebClient webClient() { return WebClient.builder() .baseUrl("http://localhost:端口号/details-service") // 替换为Service-two的实际地址和端口 .build(); } // 方式二:在调用时使用完整路径 Map<String, Object> details = client.get().uri("/details-service/details") .retrieve().bodyToFlux(new ParameterizedTypeReference<Map<String,Object>>(){}) .blockFirst();
3. 避免使用类级别的全局变量存储返回数据
finalList是类成员变量,属于全局共享状态,线程不安全且可能导致序列化异常。应在方法内部创建局部变量:
@GetMapping(value = "/products") public Map<String, Object> getMessage() { Map<String, Object> finalList = new HashMap<>(); Map<String, Object> details = client.get().uri("/details").retrieve() .bodyToFlux(new ParameterizedTypeReference<Map<String,Object>>(){}) .blockFirst(); finalList.put("details", details); return finalList; }
4. 方法返回类型改用接口而非具体实现类
将返回类型从HashMap<String, Object>改为Map<String, Object>,Spring MVC对接口类型的序列化支持更稳定:
@GetMapping(value = "/products") public Map<String, Object> getMessage() { // 原有逻辑不变 }
5. 确认Jackson依赖的正确性
Spring Boot Starter Web默认包含jackson-databind依赖,若手动添加Jackson依赖,需确保版本与Spring Boot版本匹配。检查pom.xml(Maven)或build.gradle(Gradle)中的依赖配置,避免版本冲突。
内容的提问来源于stack exchange,提问作者arj

