JDK17新特性之--JDK新的HttpClient

前言

JDK版本都升级到20了,我们还在使用JDK8,最近我们准备新项目直接升级到JDK17了,JDK9-JDK17还是有很多新功能的,
最近也在学习相关的新功能,准备写一个系列文章,主要学习JDK9-JDK17升级的新功能,本篇先学习JDK自带的HTTPClient。
新的HTTPClient是在JDK9就有了1,JDK9到JDK17使用方式略有同,我们就按最新的JDK17使用方法来学习好了。

HttpClient初始化

HttpClient的初始化有点像OKHTTP,可以通过version指定HTTP协议版本,通过connectTimeout设置超时时间,通过authenticator设置鉴权,通过proxy设置代理

        HttpClient client = HttpClient.newBuilder()
                .version(HttpClient.Version.HTTP_2)
                .connectTimeout(Duration.ofSeconds(20))
                .authenticator(new Authenticator() {
                    @Override
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication("admin", "password".toCharArray());
                    }
                })
                .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80)))
                .build();

GET请求

为了方便测试,创建一个简单的GET请求方法,接收@PathVariable、@RequestParam、@RequestHeader、@CookieValue类型参数:

    @GetMapping(value = "/user/{id}")
    public Map<String, Object> user(@PathVariable Long id,
                                    @RequestParam String userName,
                                    @RequestHeader String userHeader,
                                    HttpServletResponse response,
                                    @CookieValue("JSESSIONID") String cookie) {
    
    
        response.addHeader("X-USER-ID", id.toString());
        response.addHeader("cookie", cookie);
        return Map.of("userName", userName, "id", id, "userHeader", userHeader);
    }

请求前我们需要构建出一个HttpRequest,设置对应的URL,Header

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://127.0.0.1/user/1?userName=admin"))
                .header("userHeader", "myHeader")
                .header("cookie", "JSESSIONID=111")
                .timeout(Duration.ofSeconds(10))
                .GET()
                .build();
        

使用同步方法发送GET请求

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        response.headers().map().forEach((k, v) -> {
    
    
            System.out.println(k + "-->" + v);
        });
        System.out.println("同步请求:" + response.body());

使用异步方法发送GET,可以通过exceptionally处理异常

        CompletableFuture<Void> voidCompletableFuture = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .thenApply(HttpResponse::body)
                .exceptionally(err -> {
    
    
                    err.printStackTrace();
                    return "error" + err.getMessage();
                })
                .thenAccept(x -> System.out.println("异步结果:" + x));
        voidCompletableFuture.join();

POST表单数据

创建一个POST接口,接收表单POST数据

    @PostMapping(value = "/user")
    public User addUser(User user) {
    
    
        System.out.println(user.getId() + ":" + user.getUserName());
        return user;
    }

通过POST提交Key-Value数据

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://127.0.0.1/user"))
                .timeout(Duration.ofMinutes(2))
                .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
                .POST(HttpRequest.BodyPublishers.ofString("id=1&userName=赵云"))
                .build();
        String body = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
        System.out.println("阻塞请求结果:" + body);

POST JONS数据

写一个PostMapping 使用@RequestBody来接收JSON数据

    @PostMapping(value = "/user/update")
    public User updateUser(@RequestBody User user) {
    
    
        System.out.println(user.getId() + ":" + user.getUserName());
        return user;
    }

通过header设置Content-type为"application/json;charset=utf-8",HttpRequest.BodyPublishers.ofString()传JSON数据

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://127.0.0.1/user/update"))
                .timeout(Duration.ofMinutes(2))
                .header("Content-Type", "application/json;charset=utf-8")
                .POST(HttpRequest.BodyPublishers.ofString(
                               """
                                {"id":1,"userName":"赵云"}
                                """
                ))
                .build();
        String body = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
        System.out.println("阻塞请求结果:" + body);

上传文件

创建一个接口,模拟一个用户设置头像,接口接收用户ID和头像文件:

    @PostMapping(value = "/upload")
    public Long updateUser(@RequestParam Long userId, @RequestParam("img") MultipartFile img) throws IOException {
    
    
        System.out.println("userId:"+userId+" upload img :"+img.getName());
        img.transferTo(new File("D://1.jpg"));
        return img.getSize();
    }

HTTPClient上传文件不是很方便,需要引用httpmime 构建一个MultipartEntity,大图片会报ContentTooLongException: Content length is too long:2目前没找到比较好的方法来实现文件上传

        //引入httpmime
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
            <version>4.5.13</version>
            <scope>test</scope>
        </dependency>

        //要上传的图片
        Path sourcePath = Path.of("D:\\timg.jpg");
        String multipartFormDataBoundary = "Java11HttpClientFormBoundary";
        //构建MultipartEntity
        HttpEntity httpEntity = MultipartEntityBuilder
                .create()
                .addBinaryBody("img", sourcePath.toFile(), ContentType.IMAGE_JPEG, "1.png")
                .setBoundary(multipartFormDataBoundary)
                .build();


        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://127.0.0.1/upload?userId=1"))
                .timeout(Duration.ofMinutes(2))
                .header("Content-Type", "multipart/form-data; boundary=" + multipartFormDataBoundary)
                .POST(HttpRequest.BodyPublishers.ofInputStream(() -> {
    
    
                    try {
    
    
                    
                        return httpEntity.getContent();
                    } catch (IOException e) {
    
    
                        throw new RuntimeException(e);
                    }
                }))
                .build();
        String body = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
        System.out.println("阻塞请求结果:" + body);

下载文件

通过CompletableFuture异步下载图片:

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://mc-public.zjol.com.cn/10529082382412_hd.mp4"))
                .timeout(Duration.ofMinutes(2))
                .build();
        CompletableFuture<Path> result = client.sendAsync(request, HttpResponse.BodyHandlers.ofFile(Paths.get("D://1.mp4"))).thenApply(HttpResponse::body);
        System.out.println(result.get());

并发请求

通过CompletableFuture合并请求

        HttpClient client = HttpClient.newHttpClient();

        List<HttpRequest> requests = IntStream.range(1, 10)
                .mapToObj(x -> String.format("http://127.0.0.1/user/%s", x))
                .map(url -> HttpRequest.newBuilder(URI.create(url)))
                .map(HttpRequest.Builder::build)
                .collect(Collectors.toList());

        List<CompletableFuture<HttpResponse<String>>> futures = requests.stream()
                .map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString()))
                .collect(Collectors.toList());
        futures.forEach(e -> e.whenComplete((resp, err) -> {
    
    
            if (err != null) {
    
    
                err.printStackTrace();
            } else {
    
    
                System.out.println(resp.body());
                System.out.println(resp.statusCode());
            }
        }));
        CompletableFuture.allOf(futures.toArray(CompletableFuture<?>[]::new)).join();

websocket长连接

使用HttpClient创建WebSocket实时通信

        HttpClient client = HttpClient.newHttpClient();
        WebSocket webSocket = client.newWebSocketBuilder()
                .buildAsync(URI.create("ws://localhost:8080/hello"), new WebSocket.Listener() {
    
    
                    @Override
                    public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
    
    
                        webSocket.request(1);
                        return CompletableFuture.completedFuture(data).thenAccept(System.out::println);
                    }
                }).join();
        webSocket.sendText("hello ", false);
        webSocket.sendText("world ", true);
        TimeUnit.SECONDS.sleep(10);
        webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok").join();

参考文章


  1. JEP 110: HTTP/2 Client (Incubator) (openjdk.org) ↩︎

  2. jms - Why MultipartEntityBuilder throws org.apache.http.ContentTooLongException? - Stack Overflow; ↩︎

猜你喜欢

转载自blog.csdn.net/whzhaochao/article/details/130512864