《Java核心技术 卷II》 HTTP客户端异步处理

异步处理

异步处理响应,构建客户端时,可以提供一个执行器:

ExecutorService executor = Executors.newCachedThreadPool();
HttpClient client = HttpClient.newBuilder().executor(executor).build();

构建请求,调用sendAsync方法收到一个CompletableFuture对象。

HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build();
client.sendAsync(request,HttpResponse.BodyHandlers.ofString())
        .thenAccept(response -> {...})
HttpClient整体案例
package 第4章网络.client;

import java.io.IOException;
import java.io.Reader;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublisher;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
import java.util.Random;

public class HttpClientTest {

    public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {
        System.setProperty("jdk.httpclient.HttpClient.log", "Headers,errors");
        String propsFilename = args.length > 0 ? args[0] : "D:/ChromeCoreDownloads/eclipse-jee-2024-09-R-win32-x86_64/eclipse/eclipse-workspace/JavaCore2/src/第4章网络/client/post.properties";
        Path propsPath = Path.of(propsFilename);
        var props = new Properties();
        try (Reader in = Files.newBufferedReader(propsPath, StandardCharsets.UTF_8)) {
            props.load(in);
        }
        
        String urlString = "" + props.remove("url");
        String contentType = "" + props.remove("Content-Type");
        if(contentType.equals("multipart/form-data")) {
            var generator = new Random();
            String boundary = new BigInteger(256,generator).toString();
            contentType += ";boundary=" + boundary;
            props.replaceAll((k, v) -> v.toString().startsWith("file://")
                    ? propsPath.getParent().resolve(Path.of(v.toString().substring(7)))
                            : v);
        }
        String result = doPost(urlString, contentType, props);
        System.out.println(result);
    }
    
    public static String doPost(String url,String contentType,Map<Object,Object> data) throws IOException, URISyntaxException, InterruptedException {
        HttpClient client = HttpClient.newBuilder()
                .followRedirects(HttpClient.Redirect.ALWAYS).build();
        BodyPublisher publisher = null;
        if(contentType.startsWith("multipart/form-data")) {
            String boundary = contentType.substring(contentType.lastIndexOf("=")+1);
            publisher = MoreBodyPublishers.ofMimeMultipartData(data, boundary);
        }else if(contentType.equals("application/x-www-form-urlencoded")) {
            publisher = MoreBodyPublishers.ofFormData(data);
        }else {
            contentType = "application/json";
            publisher = MoreBodyPublishers.ofSimpleJSON(data);
        }
        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI(url))
                .header("Content-Type", contentType)
                .POST(publisher)
                .build();
        HttpResponse<String> response = 
                client.send(request, HttpResponse.BodyHandlers.ofString());
        return response.body();
    }

}

class MoreBodyPublishers {

    public static BodyPublisher ofFormData(Map<Object, Object> data) {
        boolean first = true;
        var builder = new StringBuilder();
        for (Map.Entry<Object, Object> entry : data.entrySet()) {
            if (first)
                first = false;
            else
                builder.append("&");
            builder.append(URLEncoder.encode(entry.getKey().toString(), StandardCharsets.UTF_8));
            builder.append("=");
            builder.append(URLEncoder.encode(entry.getKey().toString(), StandardCharsets.UTF_8));
        }
        return BodyPublishers.ofString(builder.toString());
    }

    private static byte[] bytes(String s) {
        return s.getBytes(StandardCharsets.UTF_8);
    }

    public static BodyPublisher ofMimeMultipartData(Map<Object, Object> data, String boundary) throws IOException {
        var byteArrays = new ArrayList<byte[]>();
        byte[] separator = bytes("--" + boundary + "\nContent-Disposition: form-data; name=");
        for (Map.Entry<Object, Object> entry : data.entrySet()) {
            byteArrays.add(separator);
            if (entry.getValue() instanceof Path path) {
                String mimeType = Files.probeContentType(path);
                byteArrays.add(bytes("\"" + entry.getKey() + "\";filename=\"" + path.getFileName()
                        + "\"\nContent-Type: " + mimeType + "\n\n"));
                byteArrays.add(Files.readAllBytes(path));
            } else {
                byteArrays.add(bytes("\"" + entry.getKey() + "\"\n\n" + entry.getValue() + "\n"));
            }
        }
        byteArrays.add(bytes("--" + boundary + "--"));
        return BodyPublishers.ofByteArrays(byteArrays);
    }

    public static BodyPublisher ofSimpleJSON(Map<Object, Object> data) {

        var builder = new StringBuilder();
        builder.append("{");
        var first = true;
        for (Map.Entry<Object, Object> entry : data.entrySet()) {
            if (first)
                first = false;
            else
                builder.append(",");
            builder.append(jsonEscape(entry.getKey().toString())).append(": ")
                    .append(jsonEscape(entry.getValue().toString()));
        }
        builder.append("}");
        return BodyPublishers.ofString(builder.toString());

    }

    private static Map<Character, String> replacements = Map.of('\b', "\\b", '\f', "\\f", '\n', "\\n", '\r', "\\r",
            '\t', "\\t", '"', "\\\"", '\\', "\\\\");

    private static StringBuilder jsonEscape(String str) {

        var result = new StringBuilder("\"");
        for (int i = 0; i < str.length(); i++) {
            char ch = str.charAt(i);
            String replacement = replacements.get(ch);
            if (replacement == null)
                result.append(ch);
            else
                result.append(replacement);
        }
        result.append("\"");
        return result;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿立聊全栈

有作用的,有闲钱的支持一点。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值