本文已参与「新人创作礼」活动.一起开启掘金创作之路。
RestTemplate请求所遇问题及解决
Java中RestTemplate请求接口使用非常频繁,经常会遇到不同的要求去修改一些参数,下面是我在开发中碰到的一些问题及解决,后续出现新的情况会继续更新。
一、跳过SSL校验,比如http协议端口使用https的443接口,这时就需要跳过SSL校验。
private RestTemplate restTemplate = new RestTemplate(RestTemplateConfig.generateHttpRequestFactory());
package com.ucmed.cloud.huawei.config;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
/**
* RestTemplate跳过SSL验证
**/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(3000);
factory.setReadTimeout(5000);
return factory;
}
public static HttpComponentsClientHttpRequestFactory generateHttpRequestFactory()
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> true;
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
new NoopHostnameVerifier());
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setSSLSocketFactory(connectionSocketFactory);
CloseableHttpClient httpClient = httpClientBuilder.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(httpClient);
return factory;
}
}
\
二、请求方式的变化影响入参的改变
常见请求方式!
其中需要注意的是Get请求携带参数时,需用Map存储并在地址后拼接
public String getMeetingRoomList(String searchType ,String keyWord ,String casOrgId) {
String url = HuaWeiProduceUrl+"/conf-portal/addressbook/rooms";
HttpHeaders requestHeaders = getHttpHeaders();
Map<String , Object> map = new HashMap<>();
map.put("id","0");
map.put("searchType",searchType);
map.put("keyWord",keyWord);
map.put("casOrgId",casOrgId);
log.info("从企业通讯录查询会议室信息接口入参:"+map.toString());
HttpEntity<String> httpEntity = new HttpEntity<>(null,requestHeaders);
try{
ResponseEntity<String> response = restTemplate.exchange(url+"?id={id}&searchType={searchType}&keyWord={keyWord}&casOrgId={casOrgId}"
,HttpMethod.GET,httpEntity,String.class,map);
log.info("从企业通讯录查询会议室信息接口出参:"+response.toString());
return response.getBody();
}catch (Exception e){
log.info("从企业通讯录查询会议室信息接口异常:"+e.toString());
return e.toString();
}
}
\
三、请求头设置
(1)传递用户名密码及basic认证,具体看要求
HttpHeaders requestHeaders = new HttpHeaders();
String account = "vdc_sdk";
String pwd = "huawei@321";
String authentication = account+":"+pwd;
requestHeaders.set("authorization", "Basic " + Base64.getEncoder().encodeToString(authentication.getBytes()));
(2)如果服务端返回字节流之类的导致乱码,可在请求头中设置MediaType为application/octet-stream
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM));
restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
(3)一般设置编码格式
MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
requestHeaders.setContentType(type);
(4)当入参为XML时编码格式
MediaType type = MediaType.parseMediaType("application/xml; charset=UTF-8");
requestHeaders.setContentType(type);
(5)当请求方式为form-data时
MediaType type = MediaType.parseMediaType("application/form-data; charset=UTF-8");
requestHeaders.setContentType(type);
(6)请求头中携带cookie
HttpHeaders headers = new HttpHeaders();
String cookie = “123”;
headers.put(HttpHeaders.COOKIE, cookie);
\
四、请求地址中出现特殊字符
String url = "http://localhost:8080/hi?para1=1#2";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
URI uri = builder.build().encode().toUri();
HttpEntity<?> entity = new HttpEntity<>(null);
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET,entity,String.class);
五、入参中存在一个文件
@PostMapping("/CloudUploadFile")
@ResponseBody
public String UploadFile(MultipartFile file, String module) throws IOException{
MultiValueMap<String, Object> bodyParams = new LinkedMultiValueMap<>();
org.springframework.core.io.Resource resource = new ByteArrayResource(file.getBytes()){
@Override
public String getFilename() {
return file.getOriginalFilename();
}
};
bodyParams.add("file", resource);
bodyParams.add("module",module);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(bodyParams, headers);
String Result= restTemplate.postForObject(ManagementURL+"/CloudUploadFile/", requestEntity, String.class);
return Result;
}
入参中存在多个文件
@PostMapping("/LexerRunUploadFile")
@ResponseBody
public String LexerRunUploadFile(HttpServletRequest request) throws IOException{
MultipartHttpServletRequest params=((MultipartHttpServletRequest) request);
List<MultipartFile> files = params.getFiles("file");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> paramsMap = new LinkedMultiValueMap<>();
String Result="";
try {
for(MultipartFile file : files) {
ByteArrayResource resource = new ByteArrayResource(file.getBytes()) {
@Override
public String getFilename() {
return file.getOriginalFilename();
}
};
paramsMap.add("files", resource);
}
// 构造请求的实体。包含body和headers的内容
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(paramsMap, headers);
Result= restTemplate.postForEntity(LexerRunUrl+"/LexerRunUploadFile/", requestEntity, String.class).getBody();
} catch (Exception e) {
e.printStackTrace();
}
return Result;
}
\
六、RestTemplate创建时的常用设置
public class SpringConfigurationDemo {
@Bean
public RestTemplate myRestTemplate() {
return RestTemplateBuilder.create()
.withClientKey("myRestTemplate")
.implementation(HttpClientImplementation.OK_HTTP)
.clearMessageConverters()
.setMessageConverter(new MappingJackson2HttpMessageConverter(), MediaType.TEXT_PLAIN)
.enableAutoQueryParams()
.connectTimeout(100)
.readTimeout(200)
.header(HttpHeaders.USER_AGENT, "MyAgent")
.build();
}
}
找了一个常用的RestTemplate设置
private RestTemplate restTemplate = new RestTemplate(new HttpsClientRequestFactory());
package com.ucmed.cloud.huawei.model;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import javax.net.ssl.*;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.cert.X509Certificate;
public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {
@Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
try {
if (!(connection instanceof HttpsURLConnection)) {
throw new RuntimeException("An instance of HttpsURLConnection is expected");
}
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
httpsConnection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
super.prepareConnection(httpsConnection, httpMethod);
} catch (Exception e) {
e.printStackTrace();
}
}
// SSLSocketFactory用于创建 SSLSockets
private static class MyCustomSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
// 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。
// 这些默认的服务的最低质量要求保密保护和服务器身份验证
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
// 返回的密码套件可用于SSL连接启用的名字
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(final Socket socket, final String host, final int port,
final boolean autoClose) throws IOException {
final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress,
final int localPort) throws
IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final InetAddress host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress,
final int localPort) throws
IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
private Socket overrideProtocol(final Socket socket) {
if (!(socket instanceof SSLSocket)) {
throw new RuntimeException("An instance of SSLSocket is expected");
}
((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"});
return socket;
}
}
}