【HTTP 通信】HttpClient 进阶篇

更新日期:2022/03/24
愿你如阳光,明媚不忧伤。

友情链接 【HTTP 通信】HttpClient 基础篇

 


1. Header 消息头

HTTP 标头字段是客户端程序和服务器在每个 HTTP 请求和响应中发送和接收的字符串列表。这些标头通常对最终用户不可见,仅由服务器和客户端应用程序处理或记录。

*************************************************************************
public static void main(String[] args) throws IOException {
    HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
    response.addHeader("Access-Control-Allow-Origin", "*");
    response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    response.addHeader("Cache-Control", "max-age=3600");
    response.addHeader("Date", "Mon, 21 Mar 2022 08:12:31 GMT");

    HeaderIterator it = response.headerIterator();
    while (it.hasNext()) {
        System.out.println(it.next());
    }
}Console--------------------------------------------------------------
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Cache-Control: max-age=3600
Date: Mon, 21 Mar 2022 08:12:31 GMT
*************************************************************************

2. RequestConfig 请求配置

  • RequestConfig.java
    用于获取和配置一些外部的网络环境,它下面有一个嵌套类 RequestConfig.Builder。

参考文献 https://www.cnblogs.com/chengxiansheng/p/13232686.html

*****************************************************************
// 是否期望能够继续:主要用于发送首先请求标头,以查看服务器是否允许(接受)请求。如果服务器显示OK,则发送100-continue,客户端继续请求正文。否则,服务器响应417(期望失败)
private boolean expectContinueEnabled;
// 设置代理 IP
private HttpHost proxy;
// InetAddress 类的对象用于 IP 地址和域名,取决于它是用主机名构造的,还是已经完成了反向主机名解析。"ouseki/192.168.2.6"
private InetAddress localAddress;
// 是否启用陈旧连接检查
private boolean staleConnectionCheckEnabled = false;
// cookie 规格
private String cookieSpec;
// 是否启用重定向
private boolean redirectsEnabled = true;
// 是否允许相对重定向
private boolean relativeRedirectsAllowed = true;
// 是否允许循环重定向
private boolean circularRedirectsAllowed;
// 设置最大重定向
private int maxRedirects = 50;
// 是否启用身份验证
private boolean authenticationEnabled = true;
// 目标首选身份验证方案
private Collection<String> targetPreferredAuthSchemes;
// 代理首选身份验证方案
private Collection<String> proxyPreferredAuthSchemes;
// 设置连接请求超时
private int connectionRequestTimeout = -1;
// 设置连接超时
private int connectTimeout = -1;
// 设置 socket 读写超时
private int socketTimeout = -1;
// 是否启用内容压缩
private boolean contentCompressionEnabled = true;
// 是否标准化 Uri
private boolean normalizeUri = true;
*****************************************************************
  • RequestConfig 应用实例
*****************************************************************
public class TestHttpClient {
    public static void main(String[] args) throws IOException {
        // 新建一个RequestConfig
        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setConnectTimeout(1)
                .setSocketTimeout(1)
                .setConnectionRequestTimeout(1)
                .build();
        // 这个超时可以设置为客户端级别,作为所有请求的默认值
        CloseableHttpClient httpclient = HttpClients.custom()
                .setDefaultRequestConfig(defaultRequestConfig)
                .build();
        System.out.println(httpclient.execute(new HttpGet("http://localhost:8080/")));
    }
}Console------------------------------------------------------
17:52:30.792 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection released: [id: 0][route: {}->http://localhost:8080][total available: 0; route allocated: 0 of 2; total allocated: 0 of 20]
Exception in thread "main" org.apache.http.conn.ConnectTimeoutException: Connect to localhost:8080 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: connect timed out
	at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:151)
	at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376)
	at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393)
	at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
	at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
	at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
	at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
	at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
	at com.ouseki.itgodroad.controller.TestHttpClient.main(TestHttpClient.java:50)
Caused by: java.net.SocketTimeoutException: connect timed out
	at java.base/java.net.PlainSocketImpl.waitForConnect(Native Method)
	at java.base/java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:107)
	at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399)
	at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242)
	at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224)
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:403)
	at java.base/java.net.Socket.connect(Socket.java:608)
	at org.apache.http.conn.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:75)
	at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
	... 10 more
*****************************************************************

3. HttpContext HTTP 执行上下文

HttpContext 功能与 java.util.Map<String, Object> 类似,它是一组任意值的集合。一个应用程序可以在请求执行之前填充上下文属性或者在请求执行完成后检查上下文。多个请求序列使用同一个 HttpContext 实例就可以让会话信息和状态信息在多个请求之间自动广播。

  • HttpClientContext.java
    可以使用 HttpClientContext 这个适配器类来简化操作上下文状态。
*************************************************************************
public class TestHttpClient extends LogBean {
    public static void main(String[] args) throws IOException {
        HttpContext context=new BasicHttpContext();
        context.setAttribute(HttpClientContext.HTTP_TARGET_HOST,new HttpHost("localhost", 8080, "http"));
        context.setAttribute(HttpClientContext.HTTP_ROUTE,new HttpRoute(new HttpHost("localhost", 80, "http")));
        HttpClientContext clientContext = HttpClientContext.adapt(context);
        System.out.println(clientContext.getTargetHost());
        System.out.println(clientContext.getHttpRoute());
    }
}Console--------------------------------------------------------------
http://localhost:8080
{}->http://localhost:80
*************************************************************************
  • 发送端
    第二次请求没有携带参数,但还是读取到了内容,因为使用了相同的上下文,第一次请求过后,就将 jsessionid 保存起来了。
*************************************************************************
public class TestHttpClient extends LogBean {
    public static void main(String[] args) throws IOException {
        HttpContext httpContext = new BasicHttpContext();
        HttpClientContext httpClientContext = HttpClientContext.adapt(httpContext);
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost("http://localhost:8080/ITGodRoad/testHttpContext.do");
        // 第一次请求,设置请求参数
        List<NameValuePair> formParams = new ArrayList<>();
        formParams.add(new BasicNameValuePair("name", "ouseki"));
        formParams.add(new BasicNameValuePair("collect", "人世间"));
        formParams.add(new BasicNameValuePair("collect", "ITGodRoad"));
        httpPost.setEntity(new UrlEncodedFormEntity(formParams));
        CloseableHttpResponse response = null;
        try {
            response = httpclient.execute(httpPost, httpClientContext);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String result = EntityUtils.toString(entity);
                System.out.println("第一次请求响应:" + result);
            }
        } finally {
            assert response != null;
            response.close();
        }

        System.out.println("=================第二次请求====================");
        // 重新创建一个 HttpPost 对象,但是此次该对象中不设置请求参数
        httpPost = new HttpPost("http://localhost:8080/ITGodRoad/testHttpContext.do");
        try {
            response = httpclient.execute(httpPost, httpClientContext);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String result = EntityUtils.toString(entity);
                System.out.println("第二次请求响应:" + result);
            }
        } finally {
            response.close();
        }
    }
}Console--------------------------------------------------------------
第一次请求响应:login success!B503DAD69B10A4BACBEF44B037F1518E
=================第二次请求====================
第二次请求响应:Having been login ouseki	B503DAD69B10A4BACBEF44B037F1518E
*************************************************************************

在这里插入图片描述

  • 目标服务端
*************************************************************************
@RequestMapping("/testHttpContext.do")
public void testHttpContext(HttpServletRequest request, HttpServletResponse response) {
    HttpSession session = request.getSession();
    String jsessionid = session.getId();
    // 从 session 中获取 name
    String name = (String) session.getAttribute("name");
    String result;
    if (name == null) {
        // 从请求参数中获取 name 的值
        name = request.getParameter("name");
        session.setAttribute("name", name);
        result = "login success!" + jsessionid;
    } else {
        result = "Having been login " + name + "\t" + jsessionid;
    }
    // 将 result 内容写回到 response 响应体中
    ServletOutputStream outputStream = null;
    PrintWriter printWriter = null;
    try {
        outputStream = response.getOutputStream();
        printWriter = new PrintWriter(outputStream);
        printWriter.write(result);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (printWriter != null) {
            printWriter.close();
        }
        try {
            if (outputStream != null) {
                outputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
*************************************************************************

4. HttpRequestRetryHandler 请求重试处理

默认情况下 HttpClient 尝试从 I/O exception 中自动恢复。默认的自动恢复机制只局限于少数已知的安全的异常。

  1. HttpClient 不会试图从任何逻辑或 HTTP 协议错误(衍生自 HttpException 类)中恢复。
  2. HttpClient 将自动重试那些被认为是幂等的方法。为了保证 HTTP 传输层的安全性,系统必须保证应用层中的 HTTP 方法是幂等的。
  3. HttpClient 将自动重试那些因传输异常而失败但是 HTTP 请求仍然会被发送到目标服务器的方法。即请求还没有完全被发送到服务器端
  • createHttpclientWithRetryHandler.java
    为了使自定义异常恢复机制有效,实现了HttpRequestRetryHandler接口
*************************************************************************
public static HttpClient createHttpclientWithRetryHandler() {
    HttpRequestRetryHandler customRetryHandler = (exception, executionCount, context) -> {
        if (executionCount >= 5) {
            // Do not retry if over max retry count
            return false;
        }
        if (exception instanceof InterruptedIOException) {
            // Timeout
            return false;
        }
        if (exception instanceof UnknownHostException) {
            // Unknown host
            return false;
        }
        if (exception instanceof SSLException) {
            // SSL handshake exception
            return false;
        }

        HttpClientContext clientContext = HttpClientContext.adapt(context);
        HttpRequest request = clientContext.getRequest();
        boolean idempotent = request instanceof HttpEntityEnclosingRequest;
        if (!idempotent) {
            // Retry if the request is considered idempotent
            return true;
        }
        return false;
    };

    return HttpClients.custom()
            .setRetryHandler(customRetryHandler)
            .build();
}
*************************************************************************

5. HttpEntity 内容实体

HTTP 消息可以携带与其相关联的请求或响应的内容实体(它们是可选的)。
使用了实体的请求被称为封闭实体请求。当执行一次封闭实体请求或者是请求成功后,响应体被当做返回结果发回客户端的时候创建实体。
HttpClient 根据其内容来源分为三种类型的实体:streamed(流式)、self-contained(自包含式)、wrapping(包装式)

  • HttpEntity.java
    HashApple.txt 是序列化后的二进制(字节)文件,需要反序列化才能正确显示其内容,乱码是对的。
*************************************************************************
public class TestHttpClient extends LogBean {

    public static void main(String[] args) throws IOException {
        FileEntity fileEntity = new FileEntity(new File("E:\\HashApple.txt"), ContentType.DEFAULT_BINARY);
        // 告知此实体是否依赖于基础流。
        System.out.println("isStreaming\t" + fileEntity.isStreaming());
        // 告知实体是否能够多次生成其数据。
        System.out.println("isRepeatable\t" + fileEntity.isRepeatable());
        /*
        告知实体是否使用分块编码。
        HttpClient将此标识只作为一个提示。当使用的HTTP协议不支持块编码,比如使用HTTP/1.0协议时,这个值就会被忽略。
        */
        System.out.println("isChunked\t" + fileEntity.isChunked());
        /*
        返回实体的内容流。
        可重复实体应为每次调用此方法创建一个新的 InputStream 实例,因此可以多次使用。
        不可重复的实体应返回相同的 InputStream 实例,因此不得多次使用。
         */
        InputStream content = fileEntity.getContent();
        System.out.println("getContent\t" + new String(content.readAllBytes()));
        // 也可以调用 EntityUtils 工具类中方法(默认字符编码为 ISO_8859_1)
        System.out.println("getContent\t" + EntityUtils.toString(fileEntity));
        /*
        获取 Content-Type 标头(如果已知)。
        如果内容编码未知,则返回 null
         */
        System.out.println("getContentType\t" + fileEntity.getContentType());
        /*
        获取内容的字节数(如果已知)。
        如果未知则为负数。如果内容长度已知但超过 Long.MAX_VALUE,则返回负数。
         */
        System.out.println("getContentLength\t" + fileEntity.getContentLength());
        /*
        获取 Content-Encoding 标头(如果已知)。
        如果内容编码未知,则返回 null
         */
        System.out.println("getContentEncoding\t" + fileEntity.getContentEncoding());
    }
}Console--------------------------------------------------------------
isStreaming	false
isRepeatable	true
isChunked	false
getContent	�� sr )com.ouseki.itgodroad.controller.HashApple        L colort Ljava/lang/String;L gramt Ljava/lang/Double;L shapeq ~ xpt redsr java.lang.Double���J)k� D valuexr java.lang.Number������  xp@i��Q�t circle
getContent	¬í sr )com.ouseki.itgodroad.controller.HashApple        L colort Ljava/lang/String;L gramt Ljava/lang/Double;L shapeq ~ xpt redsr java.lang.Double€³ÂJ)D valuexr java.lang.Number†¬•”à‹  xp@i
¸Qìt circle
getContentType	Content-Type: application/octet-stream
getContentLength	227
getContentEncoding	null
*************************************************************************

5.1 streamed(流式)

内容是从流中接收的,或者是动态生成的。流式实体是不可重复生成的。

*************************************************************************
/*
BasicHttpEntity 代表底层流的基本实体,通常是在 http 报文中获取的实体。
他只有一个空参的构造方法。刚创建时没有内容,长度为负值。需要通过 set 方法把值赋进去。
 */
InputStream is = new ByteArrayInputStream("streamed".getBytes());
BasicHttpEntity basicHttpEntity = new BasicHttpEntity();
basicHttpEntity.setContent(is);

/*
InputStreamEntity 构造方法是 InputStream,[length],[ContentType]
 */
InputStreamEntity inputStreamEntity = new InputStreamEntity(is);

/*
SerializableEntity 构造方法是 Serializable,[boolean]
 */
SerializableEntity serializableEntity = new SerializableEntity("ouseki",true);
*************************************************************************

5.2 self-contained(自包含式)

内容在内存中或通过独立于连接或其他实体的方式获得。自包含实体可以重复生成(意味着它的内容可以被多次读取)。这种类型的实体将主要被用于封闭 HTTP 请求。

*************************************************************************
/*
ByteArrayEntity 构造方法是 InputStream,[length],[ContentType]
 */
ByteArrayEntity byteArrayEntity = new ByteArrayEntity("self-contained".getBytes());

/*
FileEntity 构造方法是 File 和 [ContentType]
 */
FileEntity fileEntity = new FileEntity(new File("E:\\exercise.gif"), ContentType.IMAGE_GIF);

/*
StringEntity 构造方法是 string,[contentType],[charset]
 */
String str = "self-contained";
StringEntity stringEntity2 = new StringEntity(str, StandardCharsets.UTF_8);
*************************************************************************

5.3 wrapping(包装式)

内容是从另一个实体获取的。

*************************************************************************
/*
BufferedHttpEntity 构造方法是 HttpEntity
可以把不可以重复的实体,实现成可以重复的实体。它从提供的实体中读取内容,缓存到内容中。
 */
HttpEntity httpEntity = new StringEntity("wrapping");
BufferedHttpEntity bufferedHttpEntity = new BufferedHttpEntity(httpEntity);

/*
DecompressingEntity 构造方法是 HttpEntity,InputStreamFactory(GZIPInputStreamFactory / DeflateInputStreamFactory)
用于解压 HttpEntity 实现的通用基类。
 */
DecompressingEntity decompressingEntity = new DecompressingEntity(httpEntity,GZIPInputStreamFactory.getInstance());

/*
GzipCompressingEntity 构造方法是 HttpEntity
写入时压缩内容的包装实体。
 */
GzipCompressingEntity gzipCompressingEntity = new GzipCompressingEntity(httpEntity);
*************************************************************************

6. UrlEncodedFormEntity HTML 表单

许多应用程序需要模拟一个提交 HTML 表单的过程,HttpClient 提供了实体类 UrlEncodedFormEntity 它使用 URL 编码来编码参数,生成如下的内容:param1=value1&param2=value2

  • 发送端
*************************************************************************
public class TestHttpClient extends LogBean {
    public static void main(String[] args) throws IOException {
        List<NameValuePair> formParams = new ArrayList<>();
        formParams.add(new BasicNameValuePair("name", "ouseki"));
        formParams.add(new BasicNameValuePair("collect", "人世间"));
        formParams.add(new BasicNameValuePair("collect", "ITGodRoad"));
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, StandardCharsets.UTF_8);
        HttpPost httppost = new HttpPost("http://localhost:8080/ITGodRoad/testUrlEncodedFormEntity.do");
        httppost.setEntity(entity);
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        CloseableHttpResponse response = httpclient.execute(httppost);
        HttpEntity responseEntity = response.getEntity();
        if (responseEntity != null) {
            System.out.println("HTTPS响应内容长度为:" + responseEntity.getContentLength());
            String responseStr = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
            System.out.println("HTTPS响应内容为:" + responseStr);
        }
    }
}Console--------------------------------------------------------------
HTTPS响应内容长度为:47
10:07:19.257 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection [id: 0][route: {}->http://localhost:8080] can be kept alive for 20.0 seconds
10:07:19.257 [main] DEBUG org.apache.http.impl.conn.DefaultManagedHttpClientConnection - http-outgoing-0: set socket timeout to 0
10:07:19.257 [main] DEBUG org.apache.http.impl.conn.PoolingHttpClientConnectionManager - Connection released: [id: 0][route: {}->http://localhost:8080][total available: 1; route allocated: 1 of 2; total allocated: 1 of 20]
HTTPS响应内容为:name : [ouseki]collect : [人世间, ITGodRoad]
*************************************************************************
  • 目标服务端
    如果后端响应返回的中文乱码,肯定是编码问题,有两种解决方案:
    1.修改注解:produces = { “text/plain;charset=utf-8” }
    2.配置拦截器并设置 response.setContentType(“application/json;charset=utf-8”);
*************************************************************************
@RestController
public class Test {

    @RequestMapping(value = "/testUrlEncodedFormEntity.do", produces = { "text/plain;charset=utf-8" })
    public String test(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
        Map<String, String[]> parameterMap = request.getParameterMap();
        StringBuilder stringBuilder = new StringBuilder();
        for (String key : parameterMap.keySet()) {
            String[] value = parameterMap.get(key);
            stringBuilder.append(key).append(" : ").append(Arrays.toString(value));
        }
        return stringBuilder.toString();
    }
}
*************************************************************************

7. MultipartEntityBuilder 上传文件

  • pom.xml
    即便不引入 httpmime 依赖,也是能传输文件的,不过功能不够强大。
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.5.13</version>
</dependency>
  • 发送端
*************************************************************************
public class TestHttpClient extends LogBean {
    public static void main(String[] args) {
        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("name", "ouseki"));
        params.add(new BasicNameValuePair("age", "25"));
        URI uri = null;
        try {
            uri = new URIBuilder().setScheme("http").setHost("localhost")
                    .setPort(8080).setPath("/ITGodRoad/testDoUpload.do")
                    .addParameters(params)
                    .build();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        doUpload(uri);
    }
.........................................................................
    public static void doUpload(URI uri) {
        String filesKey = "files";
        File file = new File("E:\\uploadFiles");
        File[] files = file.listFiles();
        assert files != null;
        ClientCustomSSLUtil.doUpload(uri, filesKey, files);
    }
}
*************************************************************************
public class ClientCustomSSLUtil {
    public static void doUpload(URI uri, String filesKey, File[] files) {
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpPost httppost = new HttpPost(uri);
        CloseableHttpResponse response = null;
        try {
            MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
            for (File file : files) {
                /*
                多个文件使用同一个 key 就行,后端用数组或集合进行接收即可;防止服务端收到的文件名乱码。
                可以先将文件名 URLEncode,然后服务端拿到文件名时再 URLDecode。
                文件名其实是放在请求头的 Content-Disposition 里面进行传输的:
                如其值为 form-data; name="files"; filename="cat.jpeg"
                 */
                multipartEntityBuilder.addBinaryBody(filesKey, file, ContentType.DEFAULT_BINARY, URLEncoder.encode(file.getName(), StandardCharsets.UTF_8));
            }
            /*
            其它参数(自定义contentType)
            ContentType contentType = ContentType.create("text/plain", StandardCharsets.UTF_8);
            multipartEntityBuilder.addTextBody("name", "ouseki", contentType);
            multipartEntityBuilder.addTextBody("age", "25", contentType);
             */
            HttpEntity httpEntity = multipartEntityBuilder.build();
            httppost.setEntity(httpEntity);
            response = httpclient.execute(httppost);
            HttpEntity responseEntity = response.getEntity();
            System.out.println(("HTTPS 响应状态为:" + response.getStatusLine()));
            if (responseEntity != null) {
                System.out.println("HTTPS 响应内容长度为:" + responseEntity.getContentLength());
                String responseStr = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
                System.out.println("HTTPS 响应内容为:" + responseStr);
            }
        } catch (ParseException | IOException e) {
            e.printStackTrace();
        } finally {
            try {
                assert response != null;
                httpclient.close();
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}Console--------------------------------------------------------------
HTTPS 响应状态为:HTTP/1.1 200 
HTTPS 响应内容长度为:200
HTTPS 响应内容为:ouseki25
文件信息
	文件名:cat.jpeg	文件大小:40kb	文件类型:application/octet-stream
文件信息
	文件名:HashApple.txt	文件大小:0kb	文件类型:application/octet-stream
*************************************************************************
  • 目标服务端
*************************************************************************
@RequestMapping(value = "/testUrlEncodedFormEntity.do", produces = { "text/plain;charset=utf-8" }, method = RequestMethod.POST)
public String testDoUpload(@RequestParam("name") String name, @RequestParam("age") String age,
        @RequestParam("files") List<MultipartFile> multipartFilesList) throws UnsupportedEncodingException {
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(name).append(age);
    String fileName;
    for (MultipartFile file : multipartFilesList) {
        stringBuilder.append("\n文件信息\n");
        fileName = file.getOriginalFilename();
        if (fileName == null) {
            continue;
        }
        fileName = URLDecoder.decode(fileName, Constant.UTF_8);
        stringBuilder.append("\t文件名:").append(fileName);
        stringBuilder.append("\t文件大小:").append(file.getSize() / 1024).append("kb");
        stringBuilder.append("\t文件类型:").append(file.getContentType());
    }
    return stringBuilder.toString();
}
*************************************************************************

 

7.1 流文件上传

  • 发送端
*************************************************************************
public class TestHttpClient extends LogBean {
    public static void main(String[] args) {
        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("name", "ouseki"));
        params.add(new BasicNameValuePair("age", "25"));
        URI uri = null;
        try {
            uri = new URIBuilder().setScheme("http").setHost("localhost")
                    .setPort(8080).setPath("/ITGodRoad/testDoUploadStream.do")
                    .addParameters(params)
                    .build();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        doUploadStream(uri);
    }
.........................................................................
    public static void doUploadStream(URI uri) {
        InputStream inputStream = new ByteArrayInputStream("流文件上传".getBytes());
        CloseableHttpClient httpclient = HttpClientBuilder.create().build();
        HttpPost httppost = new HttpPost(uri);
        CloseableHttpResponse response = null;
        try {
            InputStreamEntity ise = new InputStreamEntity(inputStream);
            httppost.setEntity(ise);
            response = httpclient.execute(httppost);
            HttpEntity responseEntity = response.getEntity();
            System.out.println("HTTPS 响应状态为:" + response.getStatusLine());
            if (responseEntity != null) {
                System.out.println("HTTPS 响应内容长度为:" + responseEntity.getContentLength());
                String responseStr = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
                System.out.println("HTTPS 响应内容为:" + responseStr);
            }
        } catch (ParseException | IOException e) {
            e.printStackTrace();
        } finally {
            try {
                assert response != null;
                httpclient.close();
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}Console--------------------------------------------------------------
HTTPS 响应状态为:HTTP/1.1 200 
HTTPS 响应内容长度为:50
HTTPS 响应内容为:ouseki25输入流中的数据为:流文件上传
*************************************************************************
  • 目标服务端
*************************************************************************
@RequestMapping(value = "/testDoUploadStream.do", produces = { "text/plain;charset=utf-8" }, method = RequestMethod.POST)
public String testDoUploadStream(@RequestParam("name") String name, @RequestParam("age") String age, InputStream inputStream) throws IOException {
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(name).append(age).append("输入流中的数据为:");
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Constant.UTF_8));
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line);
    }
    return stringBuilder.toString();
}
*************************************************************************

8. ResponseHandler 响应处理程序

处理响应最简单方便的方式是使用 ResponseHandler 接口,它包含了 handleResponse(HttpResponse response) 方法。
这个方法彻底的解决了用户对于连接管理的困扰。当使用 ResponseHandler 接口时, 无论是否请求执行成功或引发异常,HttpClient 都会自动关闭来确保释放的连接返回到连接管理器。

*************************************************************************
public class TestHttpClient extends LogBean {

    public static void main(String[] args) throws IOException {
        System.out.println(customResponseHandler());
    }
.........................................................................
    public static String customResponseHandler() throws IOException {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpPost httppost = new HttpPost("http://localhost:8080/ITGodRoad/testUrlEncodedFormEntity.do");
        ResponseHandler<String> responseHandler = (response) -> {
            StatusLine statusLine = response.getStatusLine();
            HttpEntity entity = response.getEntity();
            if (statusLine.getStatusCode() >= 300) {
                throw new HttpResponseException(
                        statusLine.getStatusCode(),
                        statusLine.getReasonPhrase());
            }
            if (entity == null) {
                throw new ClientProtocolException("Response contains no content");
            }
            return "请求访问成功";
        };

        return httpclient.execute(httppost, responseHandler);
    }
}Console--------------------------------------------------------------
请求访问成功
*************************************************************************
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
c语言提供了一些库来实现HTTP服务器(httpserver)和HTTP客户端(httpclient)。 HTTP服务器是一种网络应用程序,它可以接收来自客户端的HTTP请求并返回相应的HTTP响应。使用c语言编写HTTP服务器,我们可以使用诸如libmicrohttpd、mongoose等库来实现。这些库提供了一系列函数和接口,帮助我们创建HTTP服务器并处理HTTP请求。 通过在代码中使用这些库提供的函数,我们可以创建一个HTTP服务器。服务器可以监听指定的端口,接受来自客户端的连接请求,并读取和解析HTTP请求。一旦服务器接收到请求,我们可以处理它,并根据需要返回HTTP响应。例如,我们可以将请求定向到适当的处理程序函数,然后生成和发送响应。 HTTP客户端是一种用于向HTTP服务器发送请求并接收响应的应用程序。在c语言中,我们可以使用诸如curl、libcurl等库来实现HTTP客户端。这些库提供了一组函数和接口,允许我们创建HTTP请求并发送到指定的服务器,然后接收和处理响应。 通过使用这些库,我们可以编写c代码来创建HTTP客户端。我们可以设置请求头,指定所需的HTTP方法(如GET、POST等),并发送请求到指定的服务器。一旦我们接收到响应,我们可以从中提取所需的信息,并根据需要行处理。 总之,使用c语言,我们可以使用一些库来实现HTTP服务器和HTTP客户端。这些库提供了必要的函数和接口,帮助我们创建、处理和发送HTTP请求和响应。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值