Skip to content

Latest commit

 

History

History
524 lines (410 loc) · 21.3 KB

File metadata and controls

524 lines (410 loc) · 21.3 KB

Spring Boot 2.X 项目使用 Apache HttpClient 发送请求与上传文件(简易使用)



1 摘要

java 项目中网络请求是一个常用的功能。jdk 自带的网络工具类(java.net)效率低下,因此推荐使用第三方的网络库。Apache HttpClient 库深受广大开发者的欢迎。本文将介绍在 Spring Boot 2.X 项目中简单使用基于 Apache Httpclient 4.5.X 发送请求与上传文件。

Apache HttpClient 官方文档: https://hc.apache.org/httpcomponents-client-ga/quickstart.html

2 Maven 依赖

../pom.xml
../demo-common/pom.xml
            <!-- Apache httpClient -->
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>${httpclient.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpmime</artifactId>
                <version>${httpclient.version}</version>
            </dependency>

其中的版本为:

<httpclient.version>4.5.6</httpclient.version>

3 HttpClient 网络请求工具类

../demo-common/src/main/java/com/ljq/demo/springboot/common/util/SimpleHttpClientUtil.java
package com.ljq.demo.springboot.common.util;

import org.apache.http.Consts;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Map;

/**
 * @Description: 网络请求工具类--Powered by Apache HttpClient
 * @Author: junqiang.lu
 * @Date: 2019/5/16
 */
public class SimpleHttpClientUtil implements Serializable {

    private static final long serialVersionUID = 4634161754990919271L;

    private static volatile HttpClient httpClient;

    private SimpleHttpClientUtil(){}


    /**
     * GET 方式请求
     * 参数通过 url 拼接
     *
     * @param host 请求地址
     * @param path 接口路径
     * @param paramsMap 请求参数
     * @return
     * @throws IOException
     */
    public static HttpResponse doGet(String host, String path, Map<String, String> paramsMap) throws IOException {
        initHttpClient();
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(10000)
                .setConnectTimeout(5000)
                .build();
        HttpGet httpGet = new HttpGet(getRequestUrl(host, path, paramsMap));
        httpGet.setConfig(requestConfig);
        httpGet.setHeader(HTTP.CONTENT_TYPE,ContentType.create(ContentType.APPLICATION_FORM_URLENCODED
                .getMimeType(),Consts.UTF_8).toString());

        return httpClient.execute(httpGet);
    }

    /**
     * POST 方式请求
     * 参数通过 url 拼接
     *
     * @param host 请求地址
     * @param path 接口路径
     * @param paramsMap 请求参数
     * @return
     * @throws IOException
     */
    public static HttpResponse doPost(String host, String path, Map<String, String> paramsMap) throws IOException {
        initHttpClient();
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(10000)
                .setConnectTimeout(5000)
                .build();
        HttpPost httpPost = new HttpPost(getRequestUrl(host, path, paramsMap));
        httpPost.setConfig(requestConfig);
        httpPost.setHeader(HTTP.CONTENT_TYPE,ContentType.create(ContentType.APPLICATION_FORM_URLENCODED
                .getMimeType(),Consts.UTF_8).toString());

        return httpClient.execute(httpPost);
    }

    /**
     * POST 方式请求
     * 参数通过 Body 传送,JSON 格式
     *
     * @param host 请求地址
     * @param path 接口路径
     * @param jsonParams 请求参数(json 字符串)
     * @return
     */
    public static HttpResponse doPost(String host, String path, String jsonParams) throws IOException {
        initHttpClient();
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(10000)
                .setConnectTimeout(5000)
                .build();
        HttpPost httpPost = new HttpPost(host + path);
        StringEntity stringentity = new StringEntity(jsonParams, ContentType.APPLICATION_JSON);
        httpPost.setEntity(stringentity);
        httpPost.setConfig(requestConfig);
        httpPost.addHeader(HTTP.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());

        return httpClient.execute(httpPost);
    }

    /**
     * POST 方式请求
     * (允许一个字段多个值)
     *
     * @param host
     * @param path
     * @param nameValuePairList
     * @return
     */
    public static HttpResponse doPost(String host, String path, List<NameValuePair> nameValuePairList) throws IOException {
        initHttpClient();
        HttpPost httpPost = new HttpPost(host + path);
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairList));
        httpPost.setHeader(HTTP.CONTENT_TYPE, ContentType.create(ContentType.APPLICATION_FORM_URLENCODED.getMimeType(),
                Consts.UTF_8).toString());

        return httpClient.execute(httpPost);
    }
  
    /**
     * POST 方式请求
     * 文件上传
     *
     * @param host 请求地址
     * @param path 接口路径
     * @param paramsMap 请求参数
     * @param fileInputStream 待上传文件流
     * @param name 文件对应字段名
     * @param fileOriginalName 原始文件名
     * @return
     */
    public static HttpResponse doPost(String host, String path, Map<String, String> paramsMap,
                                      InputStream fileInputStream, String name, String fileOriginalName) throws IOException {
        initHttpClient();
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(10000)
                .setConnectTimeout(5000)
                .build();
        HttpPost httpPost = new HttpPost(host + path);
        MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
        // 解决中文文件名乱码问题
        entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        entityBuilder.setCharset(Consts.UTF_8);
        ContentType contentType = ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), Consts.UTF_8);
        for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
            entityBuilder.addTextBody(entry.getKey(), entry.getValue(), contentType);
        }
        if (fileInputStream != null && name != null && fileOriginalName != null) {
            entityBuilder.addBinaryBody(name, fileInputStream, ContentType.DEFAULT_BINARY, fileOriginalName);
        }
        httpPost.setEntity(entityBuilder.build());
        httpPost.setConfig(requestConfig);

        return httpClient.execute(httpPost);
    }



    /**
     * 初始化 httpClient
     * @return
     */
    private static HttpClient initHttpClient() {
        if  (httpClient == null) {
            synchronized (SimpleHttpClientUtil.class) {
                if (httpClient == null) {
                    httpClient = HttpClients.createDefault();
                }
            }
        }
        return httpClient;
    }

    /**
     * 获取完整请求地址(包含参数)
     * 参数拼接在 url 中
     *
     * @param host 请求地址
     * @param path 接口路径
     * @param paramsMap 请求参数
     * @return
     */
    private static String getRequestUrl(String host, String path, Map<String, String> paramsMap) {
        StringBuilder reqUrl = new StringBuilder(host).append(path);
        if (paramsMap != null && !paramsMap.isEmpty()) {
            StringBuilder params = new StringBuilder();
            for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
                params.append("&" + entry.getKey() + "=" + entry.getValue());
            }
            String paramConnector = "?";
            if (!host.contains(paramConnector) && !path.contains(paramConnector)) {
                reqUrl.append(paramConnector);
                reqUrl.append(params.toString().substring(1));
            } else {
                reqUrl.append(params.toString());
            }
        }

        return reqUrl.toString();
    }


}

4 应用与测试

4.1 测试类

../demo-web/src/test/java/com/ljq/demo/springboot/common/util/SimpleHttpClientUtilTest.java
package com.ljq.demo.springboot.common.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.junit.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class SimpleHttpClientUtilTest {

    private static String API_HOST = "http://192.168.100.100:8848";
    private static String API_PATH_USER_LIST = "/api/user/list";
    private static String API_PATH_USER_LISTS = "/api/user/lists";
    private static String API_PATH_COMMON_UPLOAD = "/api/demo/common/upload";


    @Test
    public void doGet() throws IOException {
        String paramsStr = "?demoKey=demoValue";
        Map<String, String> paramsMap = new HashMap<>(16);
        paramsMap.put("demoMapKey", "demoMapValue");
        paramsMap.put("zhTest", "德玛西亚");

        HttpResponse httpResponse = SimpleHttpClientUtil.doGet(API_HOST, API_PATH_USER_LIST + paramsStr, paramsMap);

        printHttpResponse(httpResponse);
    }

    @Test
    public void doPostJson() throws IOException {
        Map<String, String> paramsMap = new HashMap<>(16);
        paramsMap.put("demoMapKey", "demoMapValue");
        paramsMap.put("zhTest", "德玛西亚");

        HttpResponse httpResponse = SimpleHttpClientUtil.doPost(API_HOST, API_PATH_USER_LIST, new ObjectMapper().writeValueAsString(paramsMap));

        printHttpResponse(httpResponse);

    }

    @Test
    public void doPostUrl() throws IOException {
        Map<String, String> paramsMap = new HashMap<>(16);
        paramsMap.put("demoMapKey", "demoMapValue");
        paramsMap.put("zhTest", "德玛西亚");

        HttpResponse httpResponse = SimpleHttpClientUtil.doPost(API_HOST, API_PATH_USER_LISTS, paramsMap);

        printHttpResponse(httpResponse);

    }

    @Test
    public void doPostNameValuePairList() throws IOException {
        List<NameValuePair> paramsList = new ArrayList<>(16);
        for (int i = 0; i < 3; i++) {
            paramsList.add(new BasicNameValuePair("demoPairKey","demoPairValue" + i));
        }
        HttpResponse httpResponse = SimpleHttpClientUtil.doPost(API_HOST, API_PATH_USER_LISTS, paramsList);

        printHttpResponse(httpResponse);

    }

    @Test
    public void doPostMultipart() throws IOException {
        Map<String, String> paramsMap = new HashMap<>(16);
        paramsMap.put("demoMapKey", "demoMapValue");
        paramsMap.put("zhTest", "德玛西亚");
        Path path = Paths.get("F:\\download\\阿里巴巴Java开发手册(详尽版).pdf");
        String name = "file";
        String fileOriginalName = "阿里巴巴Java开发手册(详尽版).pdf";

        HttpResponse httpResponse = SimpleHttpClientUtil.doPost(API_HOST, API_PATH_COMMON_UPLOAD, paramsMap,
                Files.newInputStream(path),name, fileOriginalName);

        printHttpResponse(httpResponse);

    }


    /**
     * 打印 http 请求结果
     *
     * @param httpResponse
     * @throws IOException
     */
    public static void printHttpResponse(HttpResponse httpResponse) throws IOException {
        System.out.println("response Code: " + httpResponse.getStatusLine().getStatusCode());

        System.out.println(EntityUtils.toString(httpResponse.getEntity()));
    }


}

4.2 GET 请求测试

请求日志:

2019-05-16 19:49:02:919 [http-nio-8848-exec-2] INFO  com.ljq.demo.springboot.web.acpect.LogAspect(LogAspect.java 67) -[AOP-LOG-START]
        requestMark: ee0a1d50-d943-42aa-ab63-5505f04de027
        requestIP: 192.168.100.1
        contentType:application/x-www-form-urlencoded; charset=UTF-8
        requestUrl: http://192.168.100.100:8848/api/user/list
        requestMethod: GET
        requestParams: demoKey = demoValue;zhTest = 德玛西亚;demoMapKey = demoMapValue;
        targetClassAndMethod: com.ljq.demo.springboot.web.controller.UserController#queryList

返回结果:

response Code: 200
{"code":1000,"msg":"成功","data":[{"id":5,"userName":"liming","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"liming@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":4,"userName":"lily","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"lily@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":3,"userName":"jack","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"jack@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":2,"userName":"bob","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"bob@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":1,"userName":"tom","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"tom@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1}]}

4.3 POST 请求-URL拼接参数

请求日志:

2019-05-16 20:13:28:509 [http-nio-8848-exec-4] INFO  com.ljq.demo.springboot.web.acpect.LogAspect(LogAspect.java 67) -[AOP-LOG-START]
        requestMark: c7e7d6d0-873b-440e-bb24-270abde33e7f
        requestIP: 192.168.100.1
        contentType:application/x-www-form-urlencoded; charset=UTF-8
        requestUrl: http://192.168.100.100:8848/api/user/lists
        requestMethod: POST
        requestParams: zhTest = 德玛西亚;demoMapKey = demoMapValue;
        targetClassAndMethod: com.ljq.demo.springboot.web.controller.UserController#queryLists

返回结果:

response Code: 200
{"code":1000,"msg":"成功","data":[{"id":5,"userName":"liming","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"liming@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":4,"userName":"lily","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"lily@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":3,"userName":"jack","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"jack@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":2,"userName":"bob","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"bob@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":1,"userName":"tom","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"tom@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1}]}

4.4 POST请求-JSON参数

请求日志:

2019-05-16 20:15:26:249 [http-nio-8848-exec-5] INFO  com.ljq.demo.springboot.web.acpect.LogAspect(LogAspect.java 67) -[AOP-LOG-START]
        requestMark: f94612bd-dcc9-4b7b-9c77-2b296cd00948
        requestIP: 192.168.100.1
        contentType:application/json; charset=UTF-8
        requestUrl: http://192.168.100.100:8848/api/user/list
        requestMethod: POST
        requestParams: {"zhTest":"德玛西亚","demoMapKey":"demoMapValue"}
        targetClassAndMethod: com.ljq.demo.springboot.web.controller.UserController#queryList

返回结果:

response Code: 200
{"code":1000,"msg":"成功","data":[{"id":5,"userName":"liming","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"liming@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":4,"userName":"lily","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"lily@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":3,"userName":"jack","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"jack@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":2,"userName":"bob","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"bob@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1},{"id":1,"userName":"tom","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"tom@example.com","userInsertTime":"2019-04-25 17:07:30","userUpdateTime":"2019-04-25 17:07:30","userStatus":1}]}

4.5 POST请求-NameValuePair方式

使用 NameValuePair 传递参数允许一个一个字段传多个值

请求日志:

2019-05-19 21:09:41:801 [http-nio-8848-exec-1] INFO  com.ljq.demo.springboot.web.acpect.LogAspect(LogAspect.java 68) -[AOP-LOG-START]
	requestMark: 1825ca6d-2d61-450a-ba48-aa2bbc4c1123
	requestIP: 127.0.0.1
	contentType:application/x-www-form-urlencoded; charset=UTF-8
	requestUrl: http://127.0.0.1:8848/api/user/lists
	requestMethod: POST
	requestParams: demoPairKey = [demoPairValue0, demoPairValue1, demoPairValue2];
	targetClassAndMethod: com.ljq.demo.springboot.web.controller.UserController#queryLists

返回参数:

response Code: 200
{"code":1000,"msg":"成功","data":[{"id":5,"userName":"liming","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"liming@example.com","userInsertTime":"2019-05-19 17:33:30","userUpdateTime":"2019-05-19 17:33:30","userStatus":1},{"id":4,"userName":"lily","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"lily@example.com","userInsertTime":"2019-05-19 17:33:30","userUpdateTime":"2019-05-19 17:33:30","userStatus":1},{"id":3,"userName":"jack","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"jack@example.com","userInsertTime":"2019-05-19 17:33:30","userUpdateTime":"2019-05-19 17:33:30","userStatus":1},{"id":2,"userName":"bob","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"bob@example.com","userInsertTime":"2019-05-19 17:33:30","userUpdateTime":"2019-05-19 17:33:30","userStatus":1},{"id":1,"userName":"tom","userPasscode":"ed0de7252acf2980e677bacab01bde25","userEmail":"tom@example.com","userInsertTime":"2019-05-19 17:33:30","userUpdateTime":"2019-05-19 17:33:30","userStatus":1}]}

4.6 POST请求-文件上传

请求日志:

2019-05-16 20:16:56:569 [http-nio-8848-exec-7] INFO  com.ljq.demo.springboot.web.acpect.LogAspect(LogAspect.java 67) -[AOP-LOG-START]
        requestMark: 3a4eb27e-c35b-4804-bda1-100c0a1b89d9
        requestIP: 192.168.100.1
        contentType:multipart/form-data; boundary=-e08loTIIPoPhChkhGqIWqy4J9HX7REn; charset=UTF-8
        requestUrl: http://192.168.100.100:8848/api/demo/common/upload
        requestMethod: POST
        requestParams: zhTest = 德玛西亚;demoMapKey = demoMapValue;fileSize = 1163395;fileContentType = application/octet-stream;fieldName = file;fileOriginalName = 阿里巴巴Java开发手册(详尽版).pdf;
        targetClassAndMethod: com.ljq.demo.springboot.web.controller.CommonController#upload

返回参数:

response Code: 200
{"code":1000,"msg":"成功","data":null}

5 参考资料推荐

官方文档 HttpClient Quick Start

官方文档 HttpClient Examples

解决httpclient上传文件的时候中文文件名乱码的问题

6 提交记录

commit 31e54e2b81b901658599acc7b8d981fed0457276 (HEAD -> dev, origin/master, origin/dev, origin/HEAD, master)
Author: flying9001 <flying9001@gmail.com>
Date:   Thu May 16 18:20:38 2019 +0800

    spring boot 项目中简单使用 Apache httpClient

版本回退命令

git reset --soft 31e54e2b81b901658599acc7b8d981fed0457276