Spring Boot之 RestTemplate 如何调用 HTTPS 请求

Spring Boot之 RestTemplate 如何调用 HTTPS 请求

1 具体使用

如何构造 RestTemplate,以便可以调用 HTTPS,直接上源码,如下:

@Configuration
public class RestTemplateManager {

    @Bean
    public RestTemplate httpsRestTemplate(HttpComponentsClientHttpRequestFactory httpsFactory) {
        RestTemplate restTemplate = new RestTemplate(httpsFactory);
        restTemplate.setErrorHandler(new ResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse clientHttpResponse) {
                return false;
            }

            @Override
            public void handleError(ClientHttpResponse clientHttpResponse) {
            }
        });
        return restTemplate;
    }

    @Bean(name = "httpsFactory")
    public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() throws Exception {
        CloseableHttpClient httpClient = acceptsUntrustedCertsHttpClient();
        HttpComponentsClientHttpRequestFactory httpsFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        httpsFactory.setReadTimeout(SemanticConfig.INSTANCE.getSocketTimeout());
        httpsFactory.setConnectTimeout(SemanticConfig.INSTANCE.getConnectTimeout());
        return httpsFactory;
    }

    public static CloseableHttpClient acceptsUntrustedCertsHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        HttpClientBuilder b = HttpClientBuilder.create();

        // setup a Trust Strategy that allows all certificates.
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                return true;
            }
        }).build();
        b.setSSLContext(sslContext);

        // don't check Host names, either.
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;

        // create a SSL Socket Factory, to use our weakened "trust strategy"  and create a Registry, to register it.
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

        // create connection-manager using registry allows multi-threaded use
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connMgr.setMaxTotal(200);
        connMgr.setDefaultMaxPerRoute(100);
        b.setConnectionManager(connMgr);

        return b.build();
    }
}

然后在需要使用的地方,直接注入即可

    @Autowired
    private RestTemplate restTemplate;

2 踩坑

说点踩坑的题外话

2.1 背景

历史原因,服务A需要调用服务A自身的API,此时出现来Bug,但在出现问题前只有一个commit,就是修改了RestTemplate的构造方法(以便可以调用HTTPS格式的URL)。

2.2 排查

排查初期一直很奇怪,调用请求是单例,但是每次debug发现调用的用户都会由用户a变为用户b。
但是用户属性在请求初期是正常的,待到逻辑处理中间才发现是通过请求中的session中变更的。
因为内部调用API是使用RestTemplate,此前没仔细看源码,此时怀疑可能是其复用了链接请求。

可以验证是否请求连接复用:
查看发过来的请求地址和端口是否相等,一般情况下 request 每次的请求端口不同。

 String addr = request.getRemoteAddr() + ":" + request.getRemotePort();

很明显两次是一致的,确定了HTTP请求连接的复用导致session内包含旧的用户名,导致请求的用户和session的用户不同,而出现权限不同,导致创建数据集时出现项目下无可用数据集的报错提示。

2.3 总结

后面也去查看了一下 RestTemplate 源码,确定了猜想,具体信息如下:

RestTemplate 的无参构造方法使用的 ClientHttpRequestFactory 类型实例为:SimpleClientHttpRequestFactory 的createRequest方法内openConnection是每次都新建一个新的连接。具体可见openConnection源码的注释说明。

  * <P>A new instance of {@linkplain java.net.URLConnection URLConnection} is
     * created every time when invoking the
     * {@linkplain java.net.URLStreamHandler#openConnection(URL)
     * URLStreamHandler.openConnection(URL)} method of the protocol handler for
     * this URL.</P>

但为了支持 HTTPS,此处需要RestTemplate(ClientHttpRequestFactory requestFactory) 去构造对HTTPS的兼容处理,而此处需要 引入httpClient,对于此时的RestTemplate,其内部的连接由线程池管理,每次发起的请求会最大化复用闲置的线程,而不是新建一个连接。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 可以使用Spring BootRestTemplate下载文件,方法如下: 1. 创建一个RestTemplate实例: ``` @Autowired private RestTemplate restTemplate; ``` 2. 使用exchange方法发送HTTP请求,将响应内容写入文件: ``` ResponseEntity<byte[]> response = restTemplate.exchange(url, HttpMethod.GET, null, byte[].class); byte[] file = response.getBody(); try (FileOutputStream fos = new FileOutputStream("path/to/file")) { fos.write(file); } ``` 注意: 在使用exchange方法时,需要指定HTTP方法(如GET, POST等)以及响应内容的类型(byte[]类型). 希望这些信息能帮到你! ### 回答2: 使用Spring BootRestTemplate下载文件可以通过以下步骤实现: 1. 首先,需要在Spring Boot的配置文件中配置一个RestTemplate的Bean,例如: ```java @Bean public RestTemplate restTemplate() { return new RestTemplate(); } ``` 2. 然后,使用RestTemplate的`getForObject`方法发送GET请求,将文件内容以字节数组的形式返回,例如: ```java RestTemplate restTemplate = new RestTemplate(); byte[] fileBytes = restTemplate.getForObject(fileUrl, byte[].class); ``` 其中,`fileUrl`是文件的URL地址。 3. 最后,将字节数组保存为文件,可以使用FileUtils类或者IO流的方式,例如: 使用FileUtils类: ```java FileUtils.writeByteArrayToFile(new File(filePath), fileBytes); ``` 其中,`filePath`是保存文件的路径。 使用IO流: ```java try (FileOutputStream fileOutputStream = new FileOutputStream(filePath)) { fileOutputStream.write(fileBytes); } ``` 以上就是使用Spring BootRestTemplate下载文件的简单步骤。需要注意的是,这种方式适用于小文件的下载,如果文件较大,可能会导致内存溢出,可以考虑使用`getForEntity`方法获取ResponseEntity,然后利用ResponseEntity的`getBody`方法获取输入流,并使用IO流逐行写入文件的方式进行下载,以避免内存溢出的问题。 ### 回答3: 使用Spring BootRestTemplate下载文件可以通过以下步骤实现: 1. 创建RestTemplate对象: ```java RestTemplate restTemplate = new RestTemplate(); ``` 2. 设置文件下载的请求头: ```java HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM)); HttpEntity<String> entity = new HttpEntity<>(headers); ``` 3. 发送HTTP GET请求并获取响应: ```java ResponseEntity<byte[]> response = restTemplate.exchange(url, HttpMethod.GET, entity, byte[].class); ``` 4. 检查响应状态码: ```java if (response.getStatusCode() == HttpStatus.OK) { byte[] file = response.getBody(); // 将文件保存到本地 Files.write(Paths.get("文件路径"), file); } else { // 处理请求失败的情况 } ``` 这样就可以使用RestTemplate下载文件。其中,`url`是文件的URL地址,可以是远程文件的地址或者是服务器上的文件路径。在执行下载文件的过程中,我们设置了请求头`Accept`为`application/octet-stream`,表示请求的响应应该是一个二进制文件。在获取到响应后,我们通过`ResponseEntity`的`getBody()`方法获取响应的数据,并将其保存到本地。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值