当我升级到 3.2.5 版本时,我们在 3.1.0 版本上有 Spring boot 微服务,带有 Rest 模板的 POST 请求开始失败并抛出以下异常 org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unexpected end-of - 输入 VALUE_STRING。我对比了版本升级前后的JSON数据和httpHeaders,没有发现任何差异。我仍然无法弄清楚这里的实际问题是什么?还有其他人面临同样的问题吗?
技术: 爪哇17 Spring引导版本3.2.5 httpclient5 版本 - 5.3.1 Netty 处理程序版本 - 4.1.109.Final
将 Spring boot 版本从 3.1.0 升级到 3.2.5 后,使用剩余模板的 POST Api 调用会抛出 400 错误请求,我需要修复此问题
package com.ual.xclr.common.config;
import com.ual.xclr.common.interceptor.RestTemplateReqResLoggingInterceptor;
import com.ual.xclr.common.utility.XclrUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.core5.util.Timeout;
//import org.apache.http.impl.client.HttpClients;
//import org.apache.http.impl.client.HttpClientBuilder;
//import org.apache.http.impl.client.CloseableHttpClient;
//import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
//import org.apache.http.client.config.RequestConfig;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collections;
@Configuration
@Slf4j
public class RestTemplateConfig {
@Value("${restTemplateDefaultConnectionTimeOut:600000}") // 10 Min
private int restTemplateDefaultConnectionTimeOut;
@Value("${restTemplateDefaultReadTimeOut:600000}")
private int restTemplateDefaultReadTimeOut;
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Primary
public RestTemplate getRestTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.requestFactory(this::getClientHttpRequestFactory)
.interceptors(new RestTemplateReqResLoggingInterceptor())
.build();
return restTemplate;
}
private BufferingClientHttpRequestFactory getClientHttpRequestFactory() {
try {
TrustStrategy acceptingTrustStrategy = (X509Certificate[] x509Certificates, String s) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(csf)
.build();
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory();
httpFactory.setConnectTimeout(restTemplateDefaultConnectionTimeOut);
httpFactory.setConnectionRequestTimeout(restTemplateDefaultConnectionTimeOut);
httpFactory.setHttpClient(httpClient);
return new BufferingClientHttpRequestFactory(httpFactory);
} catch (Exception e) {
log.error("There is an exception in creating RestTemplate i.e. -{}", XclrUtils.crlfInjectionNeutralizer(e, true, true));
return null; // You might want to handle this differently based on your use case
}
}
}
Rest 模板调用抛出 400 错误请求
try {
httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML));
httpHeaders.setContentType(MediaType.valueOf("application/json;charset=UTF-8"));
String xclradaptercargomailpublishurl = adapterMSKUrl + "/publishCargoMailEvent";
String compositeKey = null;
if (httpHeaders.containsKey(AppConstants.OPS_FLT_NBR)) {
StringBuilder stringBuilder = new StringBuilder();
compositeKey = stringBuilder.append(UAX_CHECKOUT_MAIL_ACK).append(AppConstants.DoubleColon)
.append(httpHeaders.get(AppConstants.OPS_CARR_IATA_CD).get(0)).append(AppConstants.DoubleColon)
.append(httpHeaders.get(AppConstants.OPS_FLT_NBR).get(0)).append(AppConstants.DoubleColon)
.append(httpHeaders.get(AppConstants.FLT_LEG_DEP_DT).get(0)).append(AppConstants.DoubleColon)
.append(httpHeaders.get(AppConstants.OPS_LEG_DEP_ARPT_IATA_CD).get(0))
.append(AppConstants.DoubleColon)
.append(httpHeaders.get(AppConstants.OPS_LEG_ARR_ARPT_IATA_CD).get(0))
.append(AppConstants.DoubleColon).append(httpHeaders.get(AppConstants.ORIG_OCCUR_NBR).get(0))
.toString();
}
XCLRMsg xclrMsg = new XCLRMsg();
xclrMsg.setKey(compositeKey);
String message = XclrUtils.convertJavaObjectToJson(cargoMailAckMessage);
log.info("Safwan Testing cargomailmsg {}",message);
xclrMsg.setMessage(message);
HttpEntity<XCLRMsg> entity = new HttpEntity<>(xclrMsg, httpHeaders);
log.info("Safwan Testing xclrmsgobj {}",xclrMsg);
log.info("Safwan Testing HttpEntity {}",entity);
restTemplate.exchange(xclradaptercargomailpublishurl, HttpMethod.POST, entity, String.class);
} catch (Exception e) { // NOSONAR
log.error("Could not publish acknowledgment to CargoMail topic - {} for headers {}", XclrUtils.crlfInjectionNeutralizer(e,true,true),XclrUtils.sanitizeHttpHeadersForLogging(httpHeaders));
}
找到了解决方案,在新的 Spring boot 版本 3.2.5 中,内容长度会自动添加到 httpHeaders 中,并且 API 调用失败并抛出 400 BAD 请求。我在每次 API 调用之前通过 Rest 模板添加了以下代码,这解决了问题
httpHeaders.remove("content-length");