RestTemplate请求所遇问题及解决

305 阅读3分钟

本文已参与「新人创作礼」活动.一起开启掘金创作之路。

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;
    }

}

\

二、请求方式的变化影响入参的改变

常见请求方式!

image.png 其中需要注意的是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;
        }
    }
}