HttpClient是Apache Jakarta Common下的子项目,是一个高效的、功能丰富的HTTP客户端编程工具包,以编程的方式通过API传输和接收HTTP消息。主要功能:

支持HTTP方法:GET, POST, PUT, DELETE, HEAD, OPTIONS等

支持HTTPS协议

支持多线程应用和连接管理,可设置最大连接数

支持KeepAlive持久连接

支持自定义Cookie策略

支持代理服务器:ngnix等

支持自动跳转

......

 

本文分享Spring集成和配置HttpClient的方法,并封装HttpService,通过添加一个REST接口,演示项目中的实际应用。


代码文件

功能要点

SpringBoot集成HttpClient

pom.xml

引入HttpClient依赖org.apache.httpcomponents包里的httpclient, httpcore, httpmime

application.yml

配置HTTP连接属性

HttpConfig.java

配置Bean: HttpClient,以及RequestConfig和HttpClientConnectionManager

封装服务HttpService

HttpService.java

处理HTTP请求参数,调用HttpClient发送请求

处理返回结果ResponseHandler

RespStr.java

RespJsonObj.java

RespJsonArr.java

RespFile.java

对HttpResponse进行处理,读取HttpEntity并对返回内容进行转换

单元测试

HttpServiceTest.java

测试HttpService发送请求和处理返回结果

功能调用

CheckController.java

增加REST接口/chk/http,发送HTTP请求并返回结果。

 

代码

Github下载:https://github.com/jextop/StarterApi/

 

SpringBoot集成Client

1. pom.xml中添加httpclient, httpcore, httpmime依赖。


    org.apache.httpcomponents
    httpclient
    4.5.2


    org.apache.httpcomponents
    httpcore
    4.4.4


    org.apache.httpcomponents
    httpmime
    4.5

2. application.yml中配置HTTP连接属性,指定最大连接数、超时时间等:

http:
  maxTotal: 100
  maxPerRoute: 20
  socketTimeout: 5000
  connectTimeout: 5000
  requestTimeout: 5000

3. HttpConfig.java中配置Bean,读取HTTP连接属性配置,声明RequestConfigHttpClientConnectionManager,并且创建HttpClient实例:

@Configuration
@ConfigurationProperties("http")
public class HttpConfig {
    private Integer maxTotal;
    private Integer maxPerRoute;
    private Integer socketTimeout;
    private Integer connectTimeout;
    private Integer requestTimeout;

    @Bean
    public HttpClientConnectionManager httpClientConnectionManager() {
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager();
        connMgr.setMaxTotal(maxTotal);
        connMgr.setDefaultMaxPerRoute(maxPerRoute);
        return connMgr;
    }

    @Bean
    public RequestConfig requestConfig() {
        return RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout)
                .setConnectionRequestTimeout(requestTimeout)
                .build();
    }

    @Bean
    public HttpClient httpClient(HttpClientConnectionManager manager, RequestConfig config) {
        return HttpClientBuilder.create()
                .setConnectionManager(manager)
                .setDefaultRequestConfig(config)
                .build();
    }

}

 

封装服务HttpService.java,处理HTTP请求参数,调用HttpClient发送请求

@Service
public class HttpService {
    @Autowired
    private HttpClient httpClient;

    public <T> T sendRequest(HttpRequestBase httpRequest, ResponseHandler<T> handler) {
        try {
            return httpClient.execute(httpRequest, handler);
        } catch (ClientProtocolException e) {
            LogUtil.error("Error when sendRequest", e.getMessage());
        } catch (IOException e) {
            LogUtil.error("Error when sendRequest", e.getMessage());
        }
        return null;
    }

    public <T> T sendHttpGet(String url, ResponseHandler<T> handler) {
        return sendRequest(new HttpGet(url), handler);
    }

    public String sendHttpGet(String url) {
        return sendHttpGet(url, new RespStr());
    }

    public <T> T sendHttpGet(String url, Map, String> headers, ResponseHandler<T> handler) {
        HttpGet httpGet = new HttpGet(url);
        fillHeaders(httpGet, headers);
        return sendRequest(httpGet, handler);
    }

    private static void fillHeaders(HttpRequestBase request, Map, String> headers) {
        for (Map.Entry, String> header : headers.entrySet()) {
            request.addHeader(header.getKey(), header.getValue());
        }
    }
}

处理返回结果,封装ResponseHandler

1. RespStr.java:读取HttpResponse返回的内容,格式化为String字符串

- 调用httpResponse.getEntiry()获取返回结果

- 调用ContentType.get()获取内容类型

- 调用ContentType.getCharset()获取编码格式

- 调用EntityUtils.toString()将返回结果格式化为字符串

public class RespStr implements ResponseHandler {
    @Override
    public String handleResponse(HttpResponse httpResponse) throws ClientProtocolException, IOException {
        HttpEntity entity = httpResponse.getEntity();
        ContentType contentType = ContentType.getOrDefault(entity);
        Charset charset = contentType.getCharset();
        return EntityUtils.toString(entity, charset);
    }
}

2. RespJsonObj.java:在返回结果为JSON对象时,转换成JSONObject返回

public class RespJsonObj implements ResponseHandler {
    @Override
    public JSONObject handleResponse(HttpResponse resp) throws ClientProtocolException, IOException {
        HttpEntity entity = resp.getEntity();
        ContentType contentType = ContentType.getOrDefault(entity);
        Charset charset = contentType.getCharset();
        String jsonStr = EntityUtils.toString(entity, charset);

        // parse JSON object
        return JsonUtil.parseObj(jsonStr);
    }
}

3. RespJsonArr.java:将HTTP请求返回结果转换成JSONArray返回

public class RespJsonArr implements ResponseHandler {
    @Override
    public JSONArray handleResponse(HttpResponse resp) throws ClientProtocolException, IOException {
        HttpEntity entity = resp.getEntity();
        ContentType contentType = ContentType.getOrDefault(entity);
        Charset charset = contentType.getCharset();
        String jsonStr = EntityUtils.toString(entity, charset);

        // parse JSON array
        return JsonUtil.parseArr(jsonStr);
    }
}

4. RespFile.java:在HTTP返回二进制文件时,从Entity中读取二进制内容,并可从Header中获取文件名称。

public class RespFile implements ResponseHandler<byte[]> {
    private static final String fileNameFlag = "attachment;fileName=";

    private byte[] bytes;
    private String fileName;

    public byte[] getBytes() {
        return bytes;
    }

    public String getFileName() {
        return fileName;
    }


    @Override
    public byte[] handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
        // Header: Content-Disposition: attachment;fileName=abc.txt
        Header header = response.getFirstHeader("Content-Disposition");
        String headerValue = header.getValue();
        if (headerValue.startsWith(fileNameFlag)) {
            fileName = headerValue.substring(fileNameFlag.length(), headerValue.length());
        }

        HttpEntity entity = response.getEntity();
        bytes = EntityUtils.toByteArray(entity);
        return bytes;
    }
}

 

单元测试,调用HttpService发送请求和处理返回结果

 Spring集成HttpClient,封装HttpService_第1张图片

功能调用

1. 增加RestController:CheckController.java

2. 增加REST接口/chk/http,发送HTTP请求并返回结果。

@GetMapping(value = "/chk/http", produces = "application/json")
public Object http() {
    String strCourse = httpService.sendHttpGet("https://edu.51cto.com/lecturer/13841865.html");
    String[] courses = StrUtil.parse(strCourse, "[1-9]\\d*人学习");

    return new HashMap, Object>() {{
        put("chk", "http");
        put("course", courses);
    }};
}

 

REST接口调用HttpSevice示例

Spring集成HttpClient,封装HttpService_第2张图片