HttpClient与RestTemplate是近期新学的技术,特以笔记写一篇博客,希望对正在学习这项技术的朋友有所帮助
服务器间相互调用的技术,是Apache提供的
是HttpClient的别名
是Spring提供的,与HttpClient的作用或者功能都是一样的,两者互为代替,但是restTemplast更为简单
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
dependency>
在SpringBoot项目的测试类中,定义一个测试方法,在这个测试方法中,编写请求百度的请求代码,最后运行测试类,测试类中的请求访问的地址是百度,模拟两个服务器之间的访问(SpringBoot中的测试方法代表一个服务器,百度是一个服务器)
/**
* HttpClient入门案例 向百度发起GET请求(不带参)
* @throws IOException
*/
@Test
void httpGet() throws IOException {
//创建Httpclient,相当于打开了浏览器
CloseableHttpClient httpClient = HttpClients.createDefault();
//创建HttpGet请求,相当于在浏览器输入地址
HttpGet httpGet = new HttpGet("http://www.baidu.com/");
CloseableHttpResponse response = null;
try {
//执行请求,相当于敲完地址后按下回车,获取响应
response = httpClient.execute(httpGet);
//判断返回状态是否为 200
if (response.getStatusLine().getStatusCode() == 200) {
//解析响应,获取数据
String content = EntityUtils.toString(response.getEntity(), "UTF-8");
System.out.println(content);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (response != null) {
//关闭资源
response.close();
}
//关闭浏览器
httpClient.close();
}
}
HttpGet(携带参数)
/**
* 带参数的GET请求
*/
@Test
void httpGet_takingGinseng() throws Exception {
//创建一个HttpClient对象,相当于打开浏览器
CloseableHttpClient httpClient = HttpClients.createDefault();
//创建URL对象,并设置请求参数
URI uri = new URIBuilder("http://www.baidu.com/s?").setParameter("wd", "java").build();
log.info("uri:" + uri);
//创建http GET请求
HttpGet httpGet = new HttpGet(uri);
//带参GET请求也可以这么写
//HttpGet httpGet = new HttpGet("http://www.baidu.com/s?wd=java");
CloseableHttpResponse response = null;
try {
//执行GET请求
response = httpClient.execute(httpGet);
//判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
//解析响应数据
String content = EntityUtils.toString(response.getEntity(), "UTF-8");
System.out.println(content);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (response != null) {
//关闭资源
response.close();
}
//关闭浏览器
httpClient.close();
}
}
HttpPost(不携带参数)
/**
* 发起POST请求
*/
@Test
void HttpPost () throws IOException, URISyntaxException {
//创建HttpClient,相当于浏览器
CloseableHttpClient httpClient = HttpClients.createDefault();
//创建Http Post请求
HttpPost httpPost = new HttpPost(new URI("http://www.oschina.net/"));
//把自己伪装成浏览器,否则开源中国会拦截访问
httpPost.setHeader("User-Agent","Mozilla/5.0 (windows NT10.0;WOW64)AppleWebKit/537.36 (KHTML,like,Gecko) chrome/56.0.2924.87 Safari/537.36");
CloseableHttpResponse response = null;
//执行请求
try {
response = httpClient.execute(httpPost);
//判断返回的状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
//解析响应数据
String content = EntityUtils.toString(response.getEntity(), "UTF-8");
System.out.println(content);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (response != null) {
//关闭资源
response.close();
}
//关闭浏览器
httpClient.close();
}
}
HttpPost(携带参数)
/**
* POST请求携带参数
* @throws IOException
*/
@Test
void httpPost_takingGinseng () throws IOException {
System.setProperty("http.proxyHost", "myProxyServer.com");System.setProperty("http.proxyPort", "80");
//创建HttpClient对象,相当于打开浏览器
CloseableHttpClient httpClient = HttpClients.createDefault();
//创建Http POST请求,访问开源中国
HttpPost httpPost = new HttpPost("http://www.oschina.net/seach");
httpPost.setHeader("User-Agent","Mozilla/5.0 (windows NT10.0;WOW64)AppleWebKit/537.36 (KHTML,like,Gecko) chrome/56.0.2924.87 Safari/537.36");
//根据开源中国的请求需求,设置post请求参数
List<NameValuePair> parameters = new ArrayList<>(0);
parameters.add(new BasicNameValuePair("scope","project"));
parameters.add(new BasicNameValuePair("q","java"));
parameters.add(new BasicNameValuePair("fromerr","8bDnUWwC"));
//构造一个form表单式的实体
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters);
//将请求实体设置到HttpPost对象中
httpPost.setEntity(formEntity);
CloseableHttpResponse response = null;
try {
//执行post请求
response = httpClient.execute(httpPost);
//判断返回的状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
//解析响应体
String content = EntityUtils.toString(response.getEntity(), "UTF-8");
System.out.println(content);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (response != null) {
//关闭资源
response.close();
}
//关闭浏览器
httpClient.close();
}
}
1.需要在SpringBoot启动类中或者配置类中将RestTemplate对象交给Spring容器管理
2.在SpringBoot项目中的pom.xml文件中添加spring-boot-starter-web依赖(这个依赖是SpringBoot整合SpringMVC)
<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-webartifactId>= dependency>
1.在SpringBoot启动类中将RestTemplate交给Spring管理
此中方式解决了Post请求乱码的问题
/**
* 注入HttpTemplate
* @return
*/
@Bean
public RestTemplate restTemplate () {
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
for (HttpMessageConverter<?> mc : list) {
if (mc instanceof StringHttpMessageConverter) {
((StringHttpMessageConverter) mc).setDefaultCharset(Charset.forName("UTF-8"));
}
}
return restTemplate;
}
2.在配置类中将RestTemplate添加到Spring容器中
注意:我这里配置类中需要参数是从application.properties中读取出来的
此中方式解决了Post请求乱码的问题
HttpClientConfig.java
package com.czxy.config;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.context.properties.ConfigurationProperties;
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.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.Charset;
import java.util.List;
/**
* @Author ScholarTang
* @Date 2019/11/28 3:41 PM
* @Desc HttpClient配置类
*/
/**
* HttpClient的配置类
*/
@Configuration
@ConfigurationProperties(prefix = "http", ignoreUnknownFields = true)
public class HttpClientConfig {
private Integer maxTotal;// 最大连接
private Integer defaultMaxPerRoute;// 每个host的最大连接
private Integer connectTimeout;// 连接超时时间
private Integer connectionRequestTimeout;// 请求超时时间
private Integer socketTimeout;// 响应超时时间
/**
* HttpClient连接池
* @return
*/
@Bean
public HttpClientConnectionManager httpClientConnectionManager() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(maxTotal);
connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
return connectionManager;
}
/**
* 注册RequestConfig
* @return
*/
@Bean
public RequestConfig requestConfig() {
return RequestConfig.custom().setConnectTimeout(connectTimeout)
.setConnectionRequestTimeout(connectionRequestTimeout).setSocketTimeout(socketTimeout)
.build();
}
/**
* 注册HttpClient
* @param manager
* @param config
* @return
*/
@Bean
public HttpClient httpClient(HttpClientConnectionManager manager, RequestConfig config) {
return HttpClientBuilder.create().setConnectionManager(manager).setDefaultRequestConfig(config)
.build();
}
/**
* 使用连接池管理连接
* @param httpClient
* @return
*/
@Bean
public ClientHttpRequestFactory requestFactory(HttpClient httpClient) {
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
/**
* 使用HttpClient来初始化一个RestTemplate
* @param requestFactory
* @return
*/
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory requestFactory) {
RestTemplate template = new RestTemplate(requestFactory);
List<HttpMessageConverter<?>> list = template.getMessageConverters();
for (HttpMessageConverter<?> mc : list) {
if (mc instanceof StringHttpMessageConverter) {
((StringHttpMessageConverter) mc).setDefaultCharset(Charset.forName("UTF-8"));
}
}
return template;
}
public Integer getMaxTotal() {
return maxTotal;
}
public void setMaxTotal(Integer maxTotal) {
this.maxTotal = maxTotal;
}
public Integer getDefaultMaxPerRoute() {
return defaultMaxPerRoute;
}
public void setDefaultMaxPerRoute(Integer defaultMaxPerRoute) {
this.defaultMaxPerRoute = defaultMaxPerRoute;
}
public Integer getConnectTimeout() {
return connectTimeout;
}
public void setConnectTimeout(Integer connectTimeout) {
this.connectTimeout = connectTimeout;
}
public Integer getConnectionRequestTimeout() {
return connectionRequestTimeout;
}
public void setConnectionRequestTimeout(Integer connectionRequestTimeout) {
this.connectionRequestTimeout = connectionRequestTimeout;
}
public Integer getSocketTimeout() {
return socketTimeout;
}
public void setSocketTimeout(Integer socketTimeout) {
this.socketTimeout = socketTimeout;
}
}
application.properties
#The config for HttpClient
http.maxTotal=300
http.defaultMaxPerRoute=50
http.connectTimeout=1000
http.connectionRequestTimeout=500
http.socketTimeout=5000
http.staleConnectionCheckEnabled=true
注意:在配置类中进行配置时idea会有如下图所示的提醒
原因:是因为在配置类中使用类@ConfigurationProperties(prefix = “http”, ignoreUnknownFields = true)注解,该注解的作用是从application.properties文件中读取对应的数据(该注解中的属性和属性值只是对针对与该配置类有用)
解决方案:在pom.xml文件中添加下面这个依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
Get请求
/**
* 模拟Get请求,请求另一个服务器的接口的数据(携带参数:分页 + 多条件模糊查询的参数)
* @throws URISyntaxException
*/
@Test
void selectLike() throws URISyntaxException {
URI uri = new URIBuilder("http://localhost:8023/pageListProducts").setParameter("page", "1").setParameter("rows", "3").setParameter("productName", null).setParameter("low", "0").setParameter("tall", "0").build();
String jsonData = this.restTemplate.getForObject(uri,String.class);
log.info("jsonData:"+jsonData);
}
Post请求
/**
* 模拟Post请求,这里是一个添加的操作,从服务器1将数据传到服务器2,然后服务器2接收数据,并储存
*/
@Test
void addProduct() {
TbProduct product = new TbProduct(null,"iPhoneXS",12999.0,new Date(),1,3);
Map<String, Object> map = new HashMap<>();
map.put("productid", null);
map.put("productName", "爱的魔力转圈圈");
map.put("productPrice", 9999.0);
map.put("productTime", new Date());
map.put("brandid", 1);
map.put("categoryid",3);
//TODO 方法 传对象传集合都可以
String jsonData = this.restTemplate.postForObject("http://localhost:8023/addProduct",map,String.class);
log.info("jsonData:"+jsonData);
}
Put请求
/**
* 模拟Put请求,这里是一个修改的操作,也是从服务器1将数据传到服务器2,然后服务器2接收数据,对根据数据对表中的记录进行修改
*/
@Test
void updateProduct () {
TbProduct product = new TbProduct(1,"RestTemplate",29999.0,new Date(),2,3);
Map<String, Object> map = new HashMap<>();
map.put("productid", 1);
map.put("productName", "爱的魔力转圈圈");
map.put("productPrice", 9999.0);
map.put("productTime", new Date());
map.put("brandid", 1);
map.put("categoryid",3);
//TODO 传对象传集合都可以
this.restTemplate.put("http://localhost:8023/updateProductById",map);
}
Delete请求
/**
* 模拟delete请求,将记录的主键通过服务器1的接口(我这里使用的SpringBoot的测试类)请求服务器2,服务器2根据ID删除记录
*/
@Test
void deletedProduct () {
this.restTemplate.delete("http://localhost:8023delAllByIds/"+1);
}