Java HTTP客户端 - 在Java呼叫API休息的徒劳方式
#教程 #braziliandevs #java #cheatsheet

在Java中开发的最大优势之一是可用于执行任何必要任务的库和框架数量。这是由于一个非常活跃和敬业的社区在创造的语言中已有将近30年。

但是,缺点之一是,我们对不同的实现,配置,使用方式和良好实践感到不知所措。这也适用于重置API(或任何其他类型的HTTP申请),因为有许多库可供选择:httpurlConnection,httpclient,httpclient,restlate和Spring webclient,以及Spring Cloud Cloud OpenFeign。< /p>

本文的目的是为每个库中的每个库中的两个实现示例 - get请求和帖子 - 为将来的查询创建“备忘单”。

所有示例都在重新定位https://github.com/afagundes/java-http-clients

boralâð21171171171171171

pron© - 条件

首先是第一件事。

以下所有示例咨询了https://jsonplaceholder.typicode.com/users中可用的相同API。阿里恩(Alion),我强烈建议那些正在学习休息并创建第一个请求的人。有终点徒劳无功à。

所选端点以以下格式返回常规列表:

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  },
  ... vários outros usuários

我们想咨询API并将返回的JSON转换为对象。为此,我们将添加以下库中的pom.xml:

<dependencies>  
    <dependency>        
        <groupId>com.fasterxml.jackson.core</groupId>  
        <artifactId>jackson-databind</artifactId>  
        <version>2.14.2</version>  
    </dependency>
</dependencies>

如果您使用的是gradle,则动词少量:

dependencies {
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2'
}

接下来,我们将创建一个record(对于使用JDK 14+的人,如果您使用以前的版本,则必须像我们的祖先一样创建POJO):

// User.java

public record User(  
        Integer id,  
        String name,  
        String username,  
        String email,  
        Address address,  
        String phone,  
        String website,  
        Company company)  
{  
    public record Address(
            String street, 
            String suite, 
            String city, 
            String zipcode, 
            Geo geo)
    {  
        public record Geo(String lat, String lng) {}  
    }  

    public record Company(String name, String catchPhrase, String bs) {}  
}

最后,为了使我们的生活更轻松,让我们创建一个功利级别,其中包含一些妈妈将对象转换为对象格式,反之亦然,以及其他功能:

// ExampleUtils.java

public class ExampleUtils {  

    private ExampleUtils() {}  

    public static final String USER_API = "https://jsonplaceholder.typicode.com/users";  
    public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();  

    public static List<User> toList(InputStream inputStream) {  
        try {  
            return OBJECT_MAPPER.readValue(inputStream, new TypeReference<>() {});  
        }  
        catch (IOException exc) {  
            throw new UncheckedIOException(exc);  
        }  
    }  

    public static User toObject(InputStream inputStream) {  
        try {  
            return OBJECT_MAPPER.readValue(inputStream, User.class);  
        }  
        catch (IOException exc) {  
            throw new UncheckedIOException(exc);  
        }  
    }  

    public static String toJson(User user) {  
        try {  
            return OBJECT_MAPPER.writeValueAsString(user);  
        }  
        catch (JsonProcessingException exc) {  
            throw new UncheckedIOException(exc);  
        }
    }

    public static User buildUser() {  
        User.Address address = new User.Address(  
                "Rua http 200",  
                "apto POST",  
                "São Paulo",  
                "00200-404",  
                new User.Address.Geo("-257422", "25566987"));  

        User.Company company = new User.Company(  
                "My Great Company",  
                "We develop software!",  
                "sofware, development, java");  

        return new User(null,  
                "Archimedes Fagundes Junior",  
                "archimedes.junior",  
                "archimedes.junior@dev.com",  
                address,  
                "11 95523-9999",  
                "https://my.company.com",  
                company);  
    }
}

想知道!不用进一步的卷发,让我们转到我们的第一个实现:类HttpurlConnection

老式的httpurlconnection

要探索的第一个实现是HttpURLConnection。自JDK 1.1以来,此实现是最古老的,并且存在。以下是一个座右铭的示例,该座右铭显示使用整个GET和辅助类别的常规列表:

public void listUsers() {  
    System.out.println("\nListing users using the old school HttpURLConnection:");  

    try {  
        URL url = new URL(ExampleUtils.USER_API);  
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();  
        httpURLConnection.setRequestMethod("GET");  
        int responseCode = httpURLConnection.getResponseCode();  

        System.out.println("HTTP status: " + responseCode);  
        System.out.println("Users returned in request: ");  

        List<User> users = ExampleUtils.toList(httpURLConnection.getInputStream());  
        users.forEach(System.out::println);  

        System.out.println("Headers:");  
        httpURLConnection.getHeaderFields().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
    }  
    catch (MalformedURLException e) {  
        throw new RuntimeException("You've entered an invalid URL here: " + ExampleUtils.USER_API);  
    }  
    catch (IOException e) {  
        throw new RuntimeException("Error processing request", e);  
    }  
}

观察包含List<User> users = ExampleUtils.toList(httpURLConnection.getInputStream())拉伸的线。在这里获得了一个InputStream并将其传递给我们的助理母亲。无法将JSON直接作为字符串。让我们继续使用Inputstream方法来其他示例

现在,让我们尝试使用母亲©每篇文章创建一个用户并显示返回:

public void createNewUser() {  
    System.out.println("\nCreating a new user using the old school HttpURLConnection:");  
    User user = ExampleUtils.buildUser();  

    try {  
        URL url = new URL(ExampleUtils.USER_API);  
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();  
        httpURLConnection.setRequestMethod("POST");  
        httpURLConnection.setRequestProperty("Content-Type", "application/json");  

        String userJson = ExampleUtils.toJson(user);  

        httpURLConnection.setDoOutput(true);  
        try (OutputStream outputStream = httpURLConnection.getOutputStream()) {  
            outputStream.write(userJson.getBytes(StandardCharsets.UTF_8));  
            outputStream.flush();  
        }  

        int responseCode = httpURLConnection.getResponseCode();  
        System.out.println("HTTP status: " + responseCode);  

        User createdUser = ExampleUtils.toObject(httpURLConnection.getInputStream());  
        System.out.println("Created new user: " + createdUser);  

        System.out.println("Headers:");  
        httpURLConnection.getHeaderFields().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
    }  
    catch (MalformedURLException e) {  
        throw new RuntimeException("You've entered an invalid URL here: " + ExampleUtils.USER_API);  
    }  
    catch (IOException e) {  
        throw new RuntimeException("Error processing request", e);  
    }  
}

要观察到两个有趣的点:

  1. 我们在请求中使用httpURLConnection.setRequestProperty("Content-Type", "application/json");添加了一个新的头
  2. 要在请求正文中发送一些值,我们必须首先使用Koud4启用止损,获取对Koud5对象的引用并在其中写入值。累人。
幸运的是,有一个更现代的解决方案:Java的 httpclient 11.

Java 11 httpclient

JDK 11于2018年9月推出,带来了一个新的简单和狂热的客户端。让我们看一下如何列出端点常规:

public void listUsers() {  
    System.out.println("\nListing users using Java 11 HttpClient:");  

    HttpClient httpClient = HttpClient.newHttpClient();  
    HttpRequest request = HttpRequest.newBuilder(URI.create(ExampleUtils.USER_API)).GET().build();  

    try {  
        HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());  

        int statusCode = response.statusCode();  
        System.out.println("HTTP status: " + statusCode);  

        System.out.println("Users returned in request: ");  
        List<User> users = ExampleUtils.toList(response.body());  
        users.forEach(System.out::println);  

        System.out.println("Headers:");  
        response.headers().map().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
    }  
    catch (IOException | InterruptedException e) {  
        throw new RuntimeException(e);  
    }  
}

该示例是将API作为InputStream的返回,以便我们可以继续使用我们的妈妈,但是如果我们想将JSON作为字符串,我们可以做:

HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

System.out.println("Response json: " + response.body());

现在让我们看看如何创建新用户:

public void createNewUser() {  
    System.out.println("\nCreating a new user using Java 11 HttpClient:");  

    User user = ExampleUtils.buildUser();  
    HttpRequest.BodyPublisher userPublisher = HttpRequest.BodyPublishers.ofString(ExampleUtils.toJson(user));  

    HttpClient httpClient = HttpClient.newHttpClient();  
    HttpRequest request = HttpRequest  
            .newBuilder(URI.create(ExampleUtils.USER_API))  
            .POST(userPublisher)  
            .setHeader("Content-Type", "application/json")  
            .build();  

    try {  
        HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());  

        int statusCode = response.statusCode();  
        System.out.println("HTTP status: " + statusCode);  

        User createdUser = ExampleUtils.toObject(response.body());  
        System.out.println("Created new user: " + createdUser);  

        System.out.println("Headers:");  
        response.headers().map().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
    }  
    catch (IOException | InterruptedException e) {  
        throw new RuntimeException(e);  
    }  
}

比您的班级更清晰,更流利

在上面的示例中,我们做了一个soma soration,也就是说,我们的应用程序将停止等待API的返回。如果我们想使用旧类HttpURLConnection进行呼叫,我们将需要创建一个新线程并在其中执行我们的方式。幸运的是,新的HttpClient类使您无需额外的努力就可以执行异步呼叫。以下摘录有一个新用户的新用途的示例:

public void createNewUserAsync() {  
    System.out.println("\nCreating a new user asynchronously using Java 11 HttpClient:");  

    User user = ExampleUtils.buildUser();  
    HttpRequest.BodyPublisher userPublisher = HttpRequest.BodyPublishers.ofString(ExampleUtils.toJson(user));  

    HttpClient httpClient = HttpClient.newHttpClient();  
    HttpRequest request = HttpRequest  
            .newBuilder(URI.create(ExampleUtils.USER_API))  
            .POST(userPublisher)  
            .setHeader("Content-Type", "application/json")  
            .build();  

    httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream())  
            .thenApply(response -> {  
                System.out.println("Http status: " + response.statusCode());  
                System.out.println("Headers:");  
                response.headers().map().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
                return response;  
            })  
            .thenApply(HttpResponse::body)  
            .thenApply(ExampleUtils::toObject)  
            .thenAccept(createdUser -> System.out.println("New user created asynchronously: " + createdUser))  
            .join();  
}

好的。我们看到了Java API中可用的实现。现在,让我们看一下两个库,一个众所周知,有些不是那么多。让我们从最著名的开始。

春季残留模板

使用Spring Boot Web创建的项目已经在工具箱中具有可用于使用的RestTemplate类。让我们看看如何与她获取用户列表:

public void listUsers() {  
    System.out.println("\nListing users using Spring RestTemplate:");  

    RestTemplate restTemplate = new RestTemplate();  
    ResponseEntity<User[]> response = restTemplate.getForEntity(URI.create(ExampleUtils.USER_API), User[].class);  

    HttpStatusCode statusCode = response.getStatusCode();  
    System.out.println("HTTP status: " + statusCode.value());  
    System.out.println("Is status code 2xx successful? " + statusCode.is2xxSuccessful());  

    System.out.println("Users returned in request: ");  
    User[] users = Objects.requireNonNull(response.getBody());  
    Arrays.stream(users).forEach(System.out::println);  

    System.out.println("Headers:");  
    response.getHeaders().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
}

以及如何在我们的API中创建新用户:

public void createNewUser() {  
    System.out.println("\nCreating a new user using Spring RestTemplate:");  

    User user = ExampleUtils.buildUser();  

    RestTemplate restTemplate = new RestTemplate();  
    ResponseEntity<User> response = restTemplate.postForEntity(URI.create(ExampleUtils.USER_API), user, User.class);  

    HttpStatusCode statusCode = response.getStatusCode();  
    System.out.println("HTTP status: " + statusCode.value());  
    System.out.println("Is status code 2xx successful? " + statusCode.is2xxSuccessful());  

    User createdUser = response.getBody();  
    System.out.println("Created new user: " + createdUser);  

    System.out.println("Headers:");  
    response.getHeaders().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
}

请注意,这次我们需要使用助手将InputStream或字符串转换为用户对象。 RESTTEMPLATE已经照顾了这部分。

在上面的示例中,我们使用母亲©All Koud11和Koud12与API互动。我们还可以使用最QueNo exchange。让我们看看如何与这个母亲创建新的常规:

public void createNewUserUsingExchangeMethod() {  
    System.out.println("\nCreating a new user using Spring RestTemplate's exchange method:");  

    RestTemplate restTemplate = new RestTemplate();  

    RequestEntity<User> request = RequestEntity  
            .post(URI.create(ExampleUtils.USER_API))  
            .contentType(MediaType.APPLICATION_JSON)  
            .header(HttpHeaders.AUTHORIZATION, "Bearer XYZ1234abc")  
            .body(ExampleUtils.buildUser());  

    ResponseEntity<User> response = restTemplate.exchange(request, User.class);  

    HttpStatusCode statusCode = response.getStatusCode();  
    System.out.println("HTTP status: " + statusCode.value());  
    System.out.println("Is status code 2xx successful? " + statusCode.is2xxSuccessful());  

    User createdUser = response.getBody();  
    System.out.println("Created new user with exchange method: " + createdUser);  

    System.out.println("Headers:");  
    response.getHeaders().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
}

春季网络电视机

仍然在春季启动世界内,但是对于那些使用 spring webflux 库来使用反应性代码的人,我们有 webclient 类。现在,我们的代码越来越少。

让我们使用WebClient列出用途:

public void listUsers() {  
    System.out.println("\nListing users using Spring WebFlux:");  

    WebClient webClient = WebClient.create(ExampleUtils.USER_API);  
    ResponseEntity<List<User>> response = webClient.get().retrieve().toEntityList(User.class).block();  

    Objects.requireNonNull(response);  
    HttpStatusCode statusCode = response.getStatusCode();  
    System.out.println("HTTP status: " + statusCode.value());  
    System.out.println("Is status code 2xx successful? " + statusCode.is2xxSuccessful());  

    System.out.println("Users returned in request: ");  
    List<User> users = Objects.requireNonNull(response.getBody());  
    users.forEach(System.out::println);  

    System.out.println("Headers:");  
    response.getHeaders().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
}

设定的线条不佳。现在变得非常好ð。

让我们看看如何创建新用户:

public void createNewUser() {  
    System.out.println("\nCreating a new user using Spring WebClient:");  

    User user = ExampleUtils.buildUser();  
    WebClient webClient = WebClient.create(ExampleUtils.USER_API);  
    ResponseEntity<User> response = webClient.post()  
            .contentType(MediaType.APPLICATION_JSON)  
            .accept(MediaType.APPLICATION_JSON)  
            .body(Mono.just(user), User.class)  
            .retrieve()  
            .toEntity(User.class)  
            .block();  

    Objects.requireNonNull(response);  
    HttpStatusCode statusCode = response.getStatusCode();  
    System.out.println("HTTP status: " + statusCode.value());  
    System.out.println("Is status code 2xx successful? " + statusCode.is2xxSuccessful());  

    User createdUser = response.getBody();  
    System.out.println("Created new user: " + createdUser);  

    System.out.println("Headers:");  
    response.getHeaders().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
}

WebClient是一个反应性库,可与数据流相机,例如Flux和Koud15。对于我们的简单示例,我们使用整个Koud16来使客户在返回实体之前等待收到请求的所有请求。但是,如果您想加深春季Webflux并了解什么是通量和单声道,我建议您查看https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux中处置的官方文档。

春天云开放式

现在,让我们上楼梯上的一些步骤,并使用一个称为 openFeign 的库,该库与 spring cloud 的职责集成在一起。。

这个概念非常简单:您创建一个接口,添加一些注释,然后我们有一个功能性的HTTP客户端。春天照顾无聊的部分。

在使用假装Cliver之前,我们需要限定它。为此,我们在应用程序的主要类中添加了注释:

@SpringBootApplication  
@EnableFeignClients
public class HttpClientFeignApplication {  

   public static void main(String[] args) {  
      SpringApplication.run(HttpClientFeignApplication.class, args);  
   }  

}

请注意,@EnableFeignClients我在上面。

现在,让我们创建我们的响应界面以与通常的API通信:

// UserClient.java

@FeignClient(name="userClient", url = ExampleUtils.USER_API)  
public interface UserClient {  

    @GetMapping  
    List<User> simpleListUsers();  

    @GetMapping  
    ResponseEntity<List<User>> listUsers();  

    @PostMapping  
    ResponseEntity<User> createNewUser(User user);  

}

正是这样,仅此而已!在这里,我们创建了三个妈妈©全部:

  1. 母亲©All simpleListUsers()仅返回我们的常规列表,好像是母亲的呼唤©whinewhere。
  2. 母亲©所有listUsers()返回我们一个ResponseEntity对象,以便我们可以检查标题,请求状态等。
  3. 母亲©所有createNewUser()创建新用户。

让我们看看如何使用它们中的每个。

而不是忘记将客户注入班级的依赖:

@Component  
public class HttpClientFeign {  

    private final UserClient userClient;  

    public HttpClientFeign(UserClient userClient) {  
        this.userClient = userClient;  
    }

    // ... other methods listed below

返回一个简单的常规列表:

public void simpleListUsers() {  
    System.out.println("\nListing users using OpenFeign client:");  
    List<User> users = userClient.simpleListUsers();  

    System.out.println("Users returned in request: ");  
    users.forEach(System.out::println);  
}

返回请求列表和信息列表:

public void listUsers() {  
    System.out.println("\nListing users using OpenFeign client:");  
    ResponseEntity<List<User>> response = userClient.listUsers();  

    int statusCode = response.getStatusCode().value();  
    System.out.println("HTTP status: " + statusCode);  

    System.out.println("Users returned in request: ");  
    List<User> users = Objects.requireNonNull(response.getBody());  
    users.forEach(System.out::println);  

    System.out.println("Headers:");  
    response.getHeaders().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
}

最后,创建一个新用户:

public void createNewUser() {  
    System.out.println("\nCreating a new user using OpenFeign client:");  

    User user = ExampleUtils.buildUser();  
    ResponseEntity<User> response = userClient.createNewUser(user);  

    int statusCode = response.getStatusCode().value();  
    System.out.println("HTTP status: " + statusCode);  

    User createdUser = response.getBody();  
    System.out.println("Created new user: " + createdUser);  

    System.out.println("Headers:");  
    response.getHeaders().forEach((header, value) -> System.out.println(header + " = " + String.join(", ", value)));  
}

简单且极其实用!

包括£o

本文没有声称是结论性的,而是我在项目上使用的库的快速咨询指南。这些库中还有更多资源可以探索。我还提到了其他有趣的库,例如Micronaut或Apache Commons的HTTP客户端,但我将留下一些链接到感兴趣的人。

如上所述,所有这些示例,包括它们的其他配置和类,都在我的Github中。

在©aximað

反思