春季6.1的新休息器
#java #spring #http

春季HTTP客户介绍

Spring Framework提供了两个不同的选择来执行HTTP请求:

  1. RESTTEMPLATE:它是十年前在春季3春季引入的。它是提供同步阻止通信的模板模式的实现。
  2. WebClient:它是Spring WebFlux库的一部分在春季发布的。它提供了流利的API,并遵循一个反应性模型。

限制板方法暴露了太多的HTTP功能,导致大量超载方法。它使用雅加达servlet API的每个请求范式采用一个线程。

WebClient是支持同步和异步调用的RESTTEMPLATE的替代。它是春季Web反应性项目的一部分。

现在春季6.1 M1版本呈现RESTCLIENT。使用与RESTTEMPLATE相同的基础结构以类似的方式工作的新同步HTTP客户端。

设置项目

我们将使用Spring Boot 3.2和Spring Web依赖关系。您可以转到Spring Initializr页面,并生成一个选择Spring Web依赖项的新项目。使用maven pom.xml将包含

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

就是这样。根本不需要弹簧反应性网络依赖性。

准备项目

这是一个简单的项目,可以熟悉RESTCLIENT,因此我们将从以前的文章中对客户Web服务进行HTTP调用。此外,由于无需运行Web容器,嵌入式tomcat将被禁用。为此,application.properties文件将包含属性

spring.main.web-application-type=none

然后,Commandlinerunner类将完成所有工作。可以在下面看到类的基本结构

@Configuration
public class Initializer implements CommandLineRunner {
    private Logger logger = 
        LoggerFactory.getLogger(Initializer.class);

    private ClientProperties properties;

    public Initializer(ClientProperties properties) {
        this.properties = properties;
    }

    public void run(String... args) throws Exception {
    }
}

在运行方法中是为了与客户端点进行交互而构造必要对象的地方。

创建一个restclient

为了创建一个RESTCLIENT的实例,我们可以使用令人信服的静态方法:

  1. create()方法委托在默认ret rest客户端中。
  2. Create(String URL)接受默认的基本URL。
  3. 创建(RESTTEMPLATE RESTTEMPLATE)根据给定的REST模板的配置初始化了新的RESTCLIENT。
  4. Builder()允许自定义带有标头,错误处理程序,拦截器和更多选项的RESTCLIENT。
  5. 构建器(RESTTEMPLATE RESTTEMPLATE)根据给定的RESTTEMPLATE的配置获得RESTCLIENT构建器。

让我们写一个用构建器方法来调用客户API的restclient。

RestClient restClient = RestClient.builder()
    .baseUrl(properties.getUrl())
    .defaultHeader(HttpHeaders.AUTHORIZATION,
        encodeBasic(properties.getUsername(), 
                    properties.getPassword())
    ).build();

让我们仔细研究以上代码:

  • baseurl方法是自称的。它为客户端设置了基本URL
  • DefaulTheader允许设置HTTP标头。还有另一种名为Defaultheaders的方法,它将消费者作为多个标题的参数。我们正在设置授权标题以通过凭据。
  • 属性是一个简单的配置属性类,用于存储请求所需的REST API数据。它是在Commmandlinerunner类中注入构造函数的。
@Configuration
@ConfigurationProperties(prefix = "application.rest.v1.customer")
public class ClientProperties {
    String url;
    String username;
    String password;
    // getter/setter omitted
}

将三个新的键值添加到应用程序属性文件

application.rest.v1.customer.url=http://localhost:8080/api/v1/customers
application.rest.v1.customer.username=user1234
application.rest.v1.customer.password=password5678

最后,用于参考的encodebasic例程

private String encodeBasic(String username, String password) {
    return "Basic "+Base64
        .getEncoder()
        .encodeToString((username+":"+password).getBytes());
}

接收数据

下一步是使用客户端发送HTTP请求并接收响应。 RESTClient为每种HTTP方法提供方法。例如,要搜索所有活跃客户,必须完成GET请求。检索方法获取响应并声明如何提取。

让我们从一个简单的情况开始,将全身作为字符串。

String data = restClient.get()
    .uri("?status={STATUS}&vip={vip}","activated", true)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .body(String.class);

logger.info(data);

URI方法可以设置HTTP参数,以通过状态和VIP过滤。第一个参数(字符串模板)是附加在RESTCLIENT中定义的基本URL上的查询字符串。第二个参数是模板的URI变量(varargs)。

我们还将媒体类型指定为JSON。输出显示在控制台中:

[{"id":6,"status":"ACTIVATED","personInfo":{"name":"name 6 surname 6","email":"organisation6@email.com","dateOfBirth":"19/07/1976"},"detailsInfo":{"info":"Customer info details 6","vip":true}}]

如果我们需要检查响应状态代码或响应标头怎么办?不用担心,方法teentity会返回响应性。

ResponseEntity response = restClient.get()
    .uri("?status={STATUS}&vip={vip}","activated", true)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .toEntity(String.class);

logger.info("Status " + response.getStatusCode());
logger.info("Headers " + response.getHeaders());

转换Json

RESTCLIENT还可以以JSON格式转换响应主体。如果在ClassPath中检测到Jackson 2图书馆或Jackson Library,Spring将自动注册MappingJackson2HttpMessageConverter或MappingJacksonHttpMessageConverter。但是您可以注册自己的消息转换器并覆盖默认设置。

在我们的情况下,响应可以直接转换为记录。例如,从API中检索特定客户:

CustomerResponse customer = restClient.get()
    .uri("/{id}",3)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .body(CustomerResponse.class);

logger.info("Customer name: " + customer.personInfo().name());

和输出提取客户名称

Customer name: name 3 surname 3

要搜索客户,我们只需要使用以下代码中所示的列表类

List<CustomerResponse> customers = restClient.get()
    .uri("?status={STATUS}&vip={vip}","activated", true)    
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .body(List.class);

logger.info("Customers size " + customers.size());

显示了客户响应的记录类以供参考

public record CustomerResponse(
    long id, 
    String status, 
    CustomerPersonInfo personInfo, 
    CustomerDetailsInfo detailsInfo) {}

public record CustomerPersonInfo(
    String name, String email, String dateOfBirth) {}

public record CustomerDetailsInfo(String info, boolean vip) {}

发布数据

要发送邮政请求,只需调用邮政方法即可。下一个代码段创建了一个新客户。

CustomerRequest customer = new CustomerRequest(
                "John Smith",
                "john.smith@mycompany.com",
                LocalDate.of(1998, 10, 25),
                "Customer detailed info here",
                true
);

ResponseEntity<Void> response = restClient.post()
                .accept(MediaType.APPLICATION_JSON)
                .body(customer)
                .retrieve()
                .toBodilessEntity();

if (response.getStatusCode().is2xxSuccessful()) {
    logger.info("Created " + response.getStatusCode());
    logger.info("New URL " + response.getHeaders().getLocation());
}

响应代码确认客户已成功创建

Created 201 CREATED
New URL http://localhost:8080/api/v1/customers/11

要验证客户的添加,可以通过Postman重述上述URL

{
    "id": 11,
    "status": "ACTIVATED",
    "personInfo": {
        "name": "John Smith",
        "email": "john.smith@mycompany.com",
        "dateOfBirth": "25/10/1998"
    },
    "detailsInfo": {
        "info": "Customer detailed info here",
        "vip": true
    }
}

当然可以使用与上一节类似的代码的RESTCLIENT来获取它。

显示了客户请求的记录类以供参考

public record CustomerRequest(
    String name,
    String email,
    LocalDate dateOfBirth,
    String info, 
    Boolean vip) { }

删除数据

制作HTTP删除请求以尝试删除资源就像调用删除方法一样简单。

ResponseEntity<Void> response = restClient.delete()
    .uri("/{id}",2)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .toBodilessEntity();

logger.info("Deleted with status " + response.getStatusCode());

值得一提的是,如果操作成功,响应主体将是空的。在这种情况下,方法可以派上用场。要删除的客户ID作为URI变量传递。

Deleted with status 204 NO_CONTENT

处理错误

如果我们尝试删除或检索不存在的客户,会发生什么?客户端点将返回404错误代码以及消息详细信息。但是,每当收到客户端错误状态(400-499)或服务器错误状态(500-599)时,RESTCLIENT将抛出RESTCLIENTEXCEPTION的子类。

要定义我们的自定义异常处理程序,有两个在不同级别上起作用的选项:

  1. 在带有defaultstatushandler方法的RESTCLIENT中(对于所有发送的HTTP请求)
  2. 对于每个HTTP请求,使用onStatus方法在调用RetReReive方法之后(此方法返回响应式接口)。

第一个在此代码段中呈现

RestClient restClient = RestClient.builder()
    .baseUrl(properties.getUrl())
    .defaultHeader(HttpHeaders.AUTHORIZATION,
                   encodeBasic(properties.getUsername(), 
                   properties.getPassword()))
    .defaultStatusHandler(
        HttpStatusCode::is4xxClientError,
        (request, response) -> {
             logger.error("Client Error Status " + 
             response.getStatusCode());
             logger.error("Client Error Body "+new 
                 String(response.getBody().readAllBytes()));
    })
    .build();

和运行删除命令行跑步后的控制台:

Client Error Status 404 NOT_FOUND
Client Error Body {"status":404,"message":"Entity Customer for id 2 was not found.","timestamp":"2023-07-23T09:24:55.4088208"}

另一个选项是实现删除操作的OnStatus方法。它优先于RESTCLCLIENT DEFAULTT处理程序行为。因此,它被覆盖了以下代码行中所证明的

ResponseEntity response = restClient.delete()
    .uri("/{id}",2)
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .onStatus(HttpStatusCode::is4xxClientError,
         (req, res) -> 
         logger.error("Couldn't delete "+res.getStatusText())
    )
    .toBodilessEntity();

    if (response.getStatusCode().is2xxSuccessful())
        logger.info("Deleted with status " + 
                     response.getStatusCode());

现在,控制台中的消息将为

 Couldn't delete Not Found

交换方法

该交换方法对于必须根据响应状态进行不同解码的响应解码的情况很有用。使用交换方法时,状态处理程序将被忽略。

在此虚拟示例代码中,响应根据状态映射到实体

SimpleResponse simpleResponse = restClient.get()
    .uri("/{id}",4)
    .accept(MediaType.APPLICATION_JSON)
    .exchange((req,res) -> 
        switch (res.getStatusCode().value()) {
            case 200 -> SimpleResponse.FOUND;
            case 404 -> SimpleResponse.NOT_FOUND;
            default -> SimpleResponse.ERROR;
        }
    );

概括

在本文中,我们介绍了带有6.1 M1+的新静止电机的主要功能。现在,您应该可以在调用REST API时执行最常见的任务,并自定义错误处理并使用低级HTTP请求/响应与Exchange。

您可以看到此新API比旧的RESTTEMPLATE更容易管理。它还避免添加反应性网络库,以防您的项目仅使用WebClient API。

感谢您阅读文章,并继续关注有关Java和Spring的更多文章。