Http客户端工具
既然微服务选择了Http,那么我们就需要考虑自己来实现对请求和响应的处理。不过开源世界已经有很多的http客户端工具,能够帮助我们做这些事情,例如:
- HttpClient
- OKHttp
- URLConnection
接下来,我们就一起了解一款比较流行的客户端工具:HttpClient
3.1.HttpClient
3.1.1.介绍
HttpClient是Apache公司的产品,是Http Components下的一个组件。
官网地址:http://hc.apache.org/index.html
特点:
- 基于标准、纯净的Java语言。实现了Http1.0和Http1.1
- 以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)
- 支持HTTPS协议。
- 通过Http代理建立透明的连接。
- 自动处理Set-Cookie中的Cookie。
Rest风格:
- 查询:GET,/user/12
- 新增:POST, /user
- 修改:PUT, /user
- 删除:DELTE, /user/12
3.1.2.使用
创建一个HttpDemo工程
- pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.clocktt.demo</groupId>
<artifactId>HttpDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- User
package cn.clocktt.pojo;
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable{
private Integer id;
private String name;
private Integer age;
private String detail;
}
- 主程序
package cn.clocktt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
- HttpClientDemo
package cn.clocktt.test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class HttpClientDemo {
}
- RestTemplateDemo
package cn.clocktt.test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RestTemplateDemo {
}
目录结构
测试HttpClient
发起get请求:
package cn.clocktt.test;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest
public class HttpClientDemo {
CloseableHttpClient httpClient;
@Before
public void init(){
httpClient = HttpClients.createDefault();
}
@Test
public void testGet() throws IOException {
HttpGet request = new HttpGet("http://www.baidu.com");
String response = httpClient.execute(request, new BasicResponseHandler());
System.out.println(response);
}
}
- 响应结果
我们将该数据复制到文本,然后修改其后缀名为html。打开之后的效果如下图。
发起Post请求:
@Test
public void testPost() throws IOException{
HttpPost request = new HttpPost("http://renxufeng.ys168.com/");
//设置请求头
request.setHeader("user-agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6788.400 QQBrowser/10.3.2727.400");
String response = httpClient.execute(request, new BasicResponseHandler());
System.out.println(response);
}
- 返回的结果是:
同样,我们粘贴到一个文本,修改后缀名为html,显示结果如图:
注意 不是所有的网站都可以使用post访问,比如我们使用post访问 http://www.baidu.com 时,会抛出 org.apache.http.client.HttpResponseException: Moved Permanently
尝试访问昨天学习SpringBoot编写的接口:http://localhost/hello
这个接口返回一个User对象
@Test
public void testGetPojo() throws IOException {
HttpGet request = new HttpGet("http://localhost/user/1");
String response = this.httpClient.execute(request, new BasicResponseHandler());
System.out.println(response);
}
我们实际得到的是一个json字符串:
{"id":1,"name":"bamboo","age":17,"detail":null}
如果想要得到对象,我们还需要手动进行Json反序列化,这一点比较麻烦。
3.1.3.Json转换工具
HttpClient请求数据后是json字符串,需要我们自己把Json字符串反序列化为对象,我们会使用JacksonJson工具来实现。
JacksonJson
是SpringMVC内置的json处理工具,其中有一个ObjectMapper
类,可以方便的实现对json的处理:
对象转json
@Test
//对象转json
public void testJson() throws JsonProcessingException {
//json处理工具
ObjectMapper objectMapper = new ObjectMapper();
User user = new User();
user.setId(1);
user.setName("竹子");
user.setAge(21);
String json = objectMapper.writeValueAsString(user);
System.out.println(json);
}
结果:
json转普通对象
@Test
//json转对象
public void testJsonToUser() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
User user = new User();
user.setId(1);
user.setName("竹子");
user.setAge(21);
//序列化
String json = objectMapper.writeValueAsString(user);
System.out.println("对象转换为json:"+json);
// 反序列化,接收两个参数:json数据,反序列化的目标类字节码
User jsonToUser = objectMapper.readValue(json, User.class);
System.out.println("json转换为对象:"+jsonToUser);
}
结果:
json转集合
json转集合比较麻烦,因为你无法同时把集合的class和元素的class同时传递到一个参数。
因此Jackson做了一个类型工厂,用来解决这个问题:
@Test
//集合转json,json转集合
public void testJsonToList() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
User user = new User();
user.setId(1);
user.setName("竹子");
user.setAge(21);
User user1 = new User();
user1.setId(2);
user1.setName("熊猫");
user1.setAge(7);
List<User> userList = new ArrayList<>();
userList.add(user);
userList.add(user1);
String json = objectMapper.writeValueAsString(userList);
//将集合转换为json
// String json = objectMapper.writeValueAsString(Arrays.asList(user, user1));
// 反序列化,接收两个参数:json数据,反序列化的目标类字节码
List<User> userList1 = objectMapper.readValue(json,objectMapper.getTypeFactory().constructCollectionType(List.class,User.class));
for(User u : userList1){
System.out.println(u);
}
}
结果:
HttpClient测试的完整代码
package cn.clocktt.test;
import cn.clocktt.pojo.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class HttpClientDemo {
private CloseableHttpClient httpClient;
@Before
public void init(){
httpClient = HttpClients.createDefault();
}
@Test
public void testGet() throws IOException {
HttpGet request = new HttpGet("http://www.baidu.com");
String response = httpClient.execute(request, new BasicResponseHandler());
System.out.println(response);
}
@Test
public void testPost() throws IOException{
// HttpPost request = new HttpPost("http://renxufeng.ys168.com/");
HttpPost request = new HttpPost("http://www.oschina.net/");
//设置请求头
request.setHeader("user-agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6788.400 QQBrowser/10.3.2727.400");
String response = httpClient.execute(request, new BasicResponseHandler());
System.out.println(response);
}
@Test
public void testGetPojo() throws IOException {
HttpGet request = new HttpGet("http://localhost/user/1");
String response = this.httpClient.execute(request, new BasicResponseHandler());
System.out.println(response);
}
@Test
//对象转json
public void testUserToJson() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
User user = new User();
user.setId(1);
user.setName("竹子");
user.setAge(21);
String json = objectMapper.writeValueAsString(user);
System.out.println(json);
}
@Test
//json转对象
public void testJsonToUser() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
User user = new User();
user.setId(1);
user.setName("竹子");
user.setAge(21);
//序列化
String json = objectMapper.writeValueAsString(user);
System.out.println("对象转换为json:"+json);
// 反序列化,接收两个参数:json数据,反序列化的目标类字节码
User jsonToUser = objectMapper.readValue(json, User.class);
System.out.println("json转换为对象:"+jsonToUser);
}
@Test
//集合转json,json转集合
public void testJsonToList() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
User user = new User();
user.setId(1);
user.setName("竹子");
user.setAge(21);
User user1 = new User();
user1.setId(2);
user1.setName("熊猫");
user1.setAge(7);
List<User> userList = new ArrayList<>();
userList.add(user);
userList.add(user1);
String json = objectMapper.writeValueAsString(userList);
System.out.println("json:"+json);
//将集合转换为json
// String json = objectMapper.writeValueAsString(Arrays.asList(user, user1));
// 反序列化,接收两个参数:json数据,反序列化的目标类字节码
List<User> userList1 = objectMapper.readValue(json,objectMapper.getTypeFactory().constructCollectionType(List.class,User.class));
for(User u : userList1){
System.out.println(u);
}
}
}
3.3.Spring的RestTemplate
Spring提供了一个RestTemplate模板工具类,对基于Http的客户端进行了封装,并且实现了对象与json的序列化和反序列化,非常方便。RestTemplate并没有限定Http的客户端类型,而是进行了抽象,目前常用的3种都有支持:
- HttpClient
- OkHttp
- JDK原生的URLConnection(默认的)
首先在项目中注册一个RestTemplate
对象,可以在启动类位置注册:
package cn.clocktt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class Application {
@Bean
public RestTemplate getBackRestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
在测试类中直接@Autowired
注入:
package cn.clocktt.test;
import cn.clocktt.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RestTemplateDemo {
@Autowired
private RestTemplate restTemplate;
@Autowired
@Test
public void restTemplateTest(){
User user = restTemplate.getForObject("http://localhost/user/1", User.class);
System.out.println(user);
}
}
- 通过RestTemplate的getForObject()方法,传递url地址及实体类的字节码,RestTemplate会自动发起请求,接收响应,并且帮我们对响应结果进行反序列化。
学习完了Http客户端工具,接下来就可以正式学习微服务了。