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客户端工具,接下来就可以正式学习微服务了。

posted @ 2019-01-12 21:27  竹子の云  阅读(424)  评论(0编辑  收藏  举报