总结:
遇到这个报错是因为: Spring-Web 的RestTemplate$HttpEntityRequestCallback.doWithRequest 发起请求时, 需要一个对应的转换器 转换body对象。如果传入的是Map,则可以通过引入Jackson依赖包来做转换。
下面是排查过程
通报错日志发现 发起http请求请求的地方报错:
org.springframework.web.client.RestClientException: No HttpMessageConverter for java.util.HashMap
at org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:961)
at com.llljjj.commons.jwt.JwtRestTemplate.getToken(JwtRestTemplate.java:166)
at com.llljjj.commons.jwt.JwtRestTemplate.getAccessToken(JwtRestTemplate.java:146)
at com.llljjj.commons.jwt.JwtRestTemplate.createRequest(JwtRestTemplate.java:96)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:735)
at com.llljjj.commons.jwt.JwtRestTemplate.doExecute(JwtRestTemplate.java:71)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:674)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:342)
at com.llljjj.xxxmmm.util.JWTUtil.jwtQuery(JWTUtil.java:74)
at com.llljjj.xxxmmm.util.JWTUtil.jwtQuery(JWTUtil.java:65)
at com.llljjj.xxxmmm.util.JWTUtil.jwtQueryList(JWTUtil.java:148)
at com.llljjj.xxxmmm.util.JWTUtil.queryWhoisRecord(JWTUtil.java:263)
at com.llljjj.xxxmmm.util.ExportUtil.packageIcpWhois(ExportUtil.java:127)
at com.llljjj.xxxmmm.util.ExportUtil.exportADomain(ExportUtil.java:85)
at com.llljjj.xxxmmm.service.task.DomainTask.parseDomain(DomainTask.java:202)
at com.llljjj.xxxmmm.service.task.DomainTask.run(DomainTask.java:85)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
发起请求的项目代码
RequestCallback requestCallback = httpEntityCallback(getAuthenticateMap(), Object.class);
ClientHttpRequest request = super.createRequest(url, method);
requestCallback.doWithRequest(request);
查看 doWithRequest方法 spring-web的源码
org.springframework.web.client.RestTemplate.HttpEntityRequestCallback#doWithRequest
该方法中 会通过获取 getMessageConverters()中的转换器,依次判断能否 转换Body 中的对象,判断代码如下:
Type requestBodyType = (this.requestEntity instanceof RequestEntity ?
((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass);
if (genericConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) {
并写入请求的Body中
genericConverter.write(requestBody, requestBodyType, requestContentType, httpRequest);
如果所有的转换器都不能转化,则会报上面的错误。
由于请求包装的body getAuthenticateMap() 是一个map,报错信息 No HttpMessageConverter for java.util.HashMap ,也显示无法转化的是Map对象。
所以 针对map类型的转换器 应该有缺失。
调试正常和异常的代码
获取转换器 可以见到缺少 MappingJackson2HttpMessageConverter 转换器
正常:
getMessageConverters() = {ArrayList@4965} size = 6
0 = {ByteArrayHttpMessageConverter@4967}
1 = {StringHttpMessageConverter@4968}
2 = {ResourceHttpMessageConverter@4969}
3 = {SourceHttpMessageConverter@4970}
4 = {AllEncompassingFormHttpMessageConverter@4971}
5 = {Jaxb2RootElementHttpMessageConverter@4972}
异常:
getMessageConverters() = {ArrayList@4941} size = 7
0 = {ByteArrayHttpMessageConverter@5122}
1 = {StringHttpMessageConverter@5123}
2 = {ResourceHttpMessageConverter@5124}
3 = {SourceHttpMessageConverter@5125}
4 = {AllEncompassingFormHttpMessageConverter@5126}
5 = {Jaxb2RootElementHttpMessageConverter@5127}
6 = {MappingJackson2HttpMessageConverter@5128}
排查spring-web 源码:
RestTemplate的构造函数 根据 jackson2Present 来设置该转换器
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
RestTemplate 的静态函数
判断jackson2Present 的值,是根据是否有存在以下两个类来设置
jackson2Present =
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
两个类分别属于两个jackson的jar包
com.fasterxml.jackson.databind.ObjectMapper 属于jar包 jackson-databind-2.9.0.pr3.jar
com.fasterxml.jackson.core.JsonGenerator 属于jar包 jackson-core-2.9.0.pr3.jar
排查到jar确实没有引入,因为最近在做安全升级,有同事把这两个jar包删除了,遗漏引入新的jar包。
重新引入了 Jackson 2.12.4版本的jar包。可以正常转换 Map 类型Body 的请求。