Spring Boot 远程调用(HTTP)

文章详细介绍了在Java中使用JDK的HttpURLConnection、ApacheCommonsHttpClient、HttpClientBuilder以及OkHttp库进行POST请求的方法,包括设置请求参数、处理响应等步骤,并提供了示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.JDK中的HttpURLConnection

使用JDK 1.8中自带的rt.jar包中的java.net中的HttpURLConnection

public static void postTest() throws Exception{
 // 1.请求URL
   String postUrl = "";
   // 2.请求参数JSON格式
   Map<String, String> parammap = new HashMap<>();;
   String json = JSON.toJSONString(map);
   // 3.创建连接与设置连接参数
   URL urlObj = new URL(postUrl);
   HttpURLConnection httpConn = (HttpURLConnection) urlObj.openConnection();
   httpConn.setRequestMethod("POST");
   httpConn.setRequestProperty("Charset", "UTF-8");
   // POST请求且JSON数据,必须设置
   httpConn.setRequestProperty("Content-Type", "application/json");
   // 打开输出流,默认是false
   httpConn.setDoOutput(true);
   // 打开输入流,默认是true,可省略
   httpConn.setDoInput(true);
   // 4.从HttpURLConnection获取输出流和写数据
   OutputStream oStream = httpConn.getOutputStream();
   oStream.write(json.getBytes());
   oStream.flush();
   // 5.发起http调用(getInputStream触发http请求)
   if (httpConn.getResponseCode() != 200) {
       throw new Exception("调用服务端异常.");
   }
   // 6.从HttpURLConnection获取输入流和读数据
   BufferedReader br = new BufferedReader(
           new InputStreamReader(httpConn.getInputStream()));
   String resultData = br.readLine();
   System.out.println("从服务端返回结果: " + resultData);
   // 7.关闭HttpURLConnection连接
   httpConn.disconnect();
 }

2.commons-httpclient中的HttpClient

引入依赖

<dependency>
  <groupId>commons-httpclient</groupId>
  <artifactId>commons-httpclient</artifactId>
</dependency>
public static void postTest() throws Exception {
  // 1.请求URL
  String postUrl = "";
  // 2.请求参数
  Map<String, String> parammap = new HashMap<>();
  String json = JSON.toJSONString(parammap);
  // 3.创建连接与设置连接参数
  HttpClient httpClient = new HttpClient();
  PostMethod postMethod = new PostMethod(postUrl);
  postMethod.addRequestHeader("Content-Type", "application/json");
  RequestEntity entity = new StringRequestEntity(json, "application/json", "UTF-8");
  postMethod.setRequestEntity(entity);
  //解决返回值中文乱码
  postMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
  String resultData = "";
  // 4.发起请求
  int code = httpClient.executeMethod(postMethod);
  if (code != 200) {
      throw new Exception("调用服务端异常.");
  }
  // 5.接收返回值
  resultData = postMethod.getResponseBodyAsString();
  System.out.println("从服务端返回结果: " + resultData);
  // 6.关闭连接
  postMethod.releaseConnection();
 }

3.httpclient中的HttpClientBuilder

HttpClient:是apache httpClient包下的,代码复杂,需要资源回收。

引入依赖

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
</dependency>
public static void postTest() throws Exception {
   // 1.请求URL
   String postUrl = "";
   // 2.请求参数
   Map<String, String> paramMap = new HashMap<>();
   String json = JSON.toJSONString(paramMap);
   // 3.创建连接与设置连接参数
   CloseableHttpClient httpClient = HttpClientBuilder.create().build();
   HttpPost httpPost = new HttpPost(postUrl);
   StringEntity entity = new StringEntity(json);
   entity.setContentEncoding("UTF-8");
   entity.setContentType("application/json");
   httpPost.setEntity(entity);
   // 4.发起请求与接收返回值
   HttpResponse response = httpClient.execute(httpPost);
   if (response.getStatusLine().getStatusCode() != 200) {
       throw new Exception("调用服务端异常.");
   }
   HttpEntity res = response.getEntity();
   String resultData = EntityUtils.toString(res);
   System.out.println("从服务端返回结果: " + resultData);
   // 5.关闭连接
   httpClient.close();
 }

4.okhttp中的OkHttpClient 

引入依赖

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <exclusions>
    <exclusion>
      <groupId>com.google.android</groupId>
      <artifactId>android</artifactId>
    </exclusion>
  </exclusions>
</dependency>
public static void postTest() throws Exception {
  // 1.请求URL
  String postUrl = "";
  // 2.请求参数
  Map<String, String> paramMap = new HashMap<>();
  String json = JSON.toJSONString(paramMap);
  // 3.创建连接与设置连接参数
  MediaType mediaType = MediaType.parse("application/json; charset=UTF-8");
  RequestBody requestBody = RequestBody.Companion.create(json, mediaType);
  Request request = new Request.Builder().url(postUrl).post(requestBody).build();
  OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
  // 4.发起请求与接收返回值
  Response response = okHttpClient.newCall(request).execute();
  String resultData = response.body().string();
  System.out.println("从服务端返回结果: " + resultData);
 }

5.RestTemplate

RestTemplate:RestTemplate 是 Spring 提供的一个经典同步 HTTP 客户端工具,可以用于调用 RESTful 风格的外部接口,代码简单,默认依赖jdk的HTTP连接工具。

RestTemplate restTemplate = new RestTemplate();

String url = "http://api.example.com/user/{id}";
Map<String, String> params = new HashMap<>();
params.put("id", "123");

User user = restTemplate.getForObject(url, User.class, params);

使用 RestTemplate并发调用:

public class RestTemplateConcurrentExample {

    private RestTemplate restTemplate = new RestTemplate();

    public void fetchMultipleUsers(String[] userIds) {
        ExecutorService executor = Executors.newFixedThreadPool(userIds.length);
        for (String userId : userIds) {
            executor.submit(() -> {
                String url = "https://api.example.com/users/" + userId;
                String response = restTemplate.getForObject(url, String.class);
                System.out.println(response);
            });
        }
        executor.shutdown();
    }
}

6.WebClient

WebClient 是 Spring 5 引入的一种非阻塞式、响应式的 HTTP 客户端工具,它提供了一套简洁的 API 来发送 HTTP 请求并处理响应。WebClient 基于 Reactor 提供了对响应式编程的支持,可以实现高性能的异步操作。

1.简单使用

WebClient webClient = WebClient.create();

String url = "http://api.example.com/user/{id}";
Map<String, String> params = new HashMap<>();
params.put("id", "123");

Mono<User> result = webClient.get()
        .uri(uriBuilder -> uriBuilder.path(url).build(params))
        .retrieve()
        .bodyToMono(User.class);

result.subscribe(user -> {
   
    // 处理响应结果
});

webclient并发调用:

public class WebClientConcurrentExample {

    private WebClient webClient = WebClient.create();

    public Flux<String> fetchMultipleUsers(String[] userIds) {
        return Flux.fromArray(userIds)
               .flatMap(userId -> webClient.get()
                       .uri("https://api.example.com/users/" + userId)
                       .retrieve()
                       .bodyToMono(String.class));
    }
}

2.高级使用

1.配置连接池

/**
WebClient连接池
**/
@Configuration
public class WebClientConfig {

    @Bean
    public WebClient webClient() {
        // 配置HTTP连接池
        ConnectionProvider provider = ConnectionProvider.builder("custom")
                .maxConnections(500)
                .maxIdleTime(Duration.ofSeconds(20))
                .build();

        // 配置HTTP客户端
        HttpClient httpClient = HttpClient.create(provider)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .responseTimeout(Duration.ofSeconds(5))
                .doOnConnected(conn ->
                        conn.addHandlerLast(new ReadTimeoutHandler(5))
                                .addHandlerLast(new WriteTimeoutHandler(5)));

        // 构建WebClient实例
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .baseUrl("https://echo.apifox.com")
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                // 添加请求日志记录功能
                .filter(ExchangeFilterFunction.ofRequestProcessor(
                        clientRequest -> {
                            log.debug("Request: {} {}",
                                    clientRequest.method(),
                                    clientRequest.url());
                            return Mono.just(clientRequest);
                        }
                ))
                // 添加响应日志记录功能
                .filter(ExchangeFilterFunction.ofResponseProcessor(
                        clientResponse -> {
                            log.debug("Response status: {}",
                                    clientResponse.statusCode());
                            return Mono.just(clientResponse);
                        }
                ))
                .build();
    }
}

2. retrieve()和exchange()区别

retrieve()

  • 用途:retrieve() 方法用于简化响应处理,特别是当你只需要响应体时。
  • 自动错误处理:retrieve() 会自动处理 HTTP 错误状态码(例如 4xx 和 5xx),并抛出 WebClientResponseException 及其子类。
  • 返回值:通常用于直接获取响应体,例如 bodyToMono(String.class) 或 bodyToFlux(String.class)。
  • 适用场景:适用于大多数常见的请求处理场景,特别是当你不需要手动处理响应状态码时。
public Mono<JSONObject> get(String q1) {
    return webClient.get()
            .uri(uriBuilder -> uriBuilder
                    .path("/get")
                    .queryParam("q1", q1)
                    .build())
            .accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(JSONObject.class);
}

exchange()

  • 用途:exchange() 方法提供了更底层的控制,允许你手动处理响应,包括响应状态码和响应头。
  • 手动错误处理:exchange() 不会自动处理 HTTP 错误状态码,你需要手动检查响应状态码并进行相应的处理。
  • 返回值:返回 ClientResponse 对象,你可以从中提取响应状态码、响应头和响应体。
  • 适用场景:适用于需要手动处理响应状态码或响应头的复杂场景。
public Mono<JSONObject> get(String q1) {
    return webClient.get()
            .uri(uriBuilder -> uriBuilder
                    .path("/get")
                    .queryParam("q1", q1)
                    .build())
            .accept(MediaType.APPLICATION_JSON)
            .exchangeToMono(response -> {
                if (response.statusCode().is2xxSuccessful()) {
                    return response.bodyToMono(JSONObject.class);
                } else {
                    return Mono.error(new RuntimeException("Request failed with status code: " + response.statusCode()));
                }
            });
}

3. GET,POST,PUT,DELETE请求

@Service
public class ApiService {

    @Resource
    private WebClient webClient;

    // GET请求
    public Mono<JSONObject> get(String q1) {
        return webClient.get()
                .uri(uriBuilder -> uriBuilder
                        .path("/get")
                        .queryParam("q1", q1)
                        .build())
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(JSONObject.class);
    }

    // POST请求
    public Mono<JSONObject> post(JSONObject body) {
        return webClient.post()
                .uri("/post")
                .bodyValue(body)
                .retrieve()
                .bodyToMono(JSONObject.class);
    }

    // PUT请求
    public Mono<JSONObject> put(String q1, JSONObject JSONObject) {
        return webClient.put()
                .uri(uriBuilder -> uriBuilder
                        .path("/put")
                        .queryParam("q1", q1)
                        .build())
                .bodyValue(JSONObject)
                .retrieve()
                .bodyToMono(JSONObject.class);
    }

    // DELETE请求
    public Mono<JSONObject> delete(String q1) {
        return webClient.delete()
                .uri(uriBuilder -> uriBuilder
                        .path("/delete")
                        .queryParam("q1", q1)
                        .build())
                .retrieve()
                .bodyToMono(JSONObject.class);
    }
}

4.异常处理

onStatus:
用于处理HTTP响应状态码,允许根据不同状态码执行特定逻辑。例如,当收到404状态码时重定向到错误页面,或根据200状态码正常处理数据。 ‌
onErrorResume:
用于处理异常情况(如超时、连接失败等),提供备用响应。例如,当请求超时时返回默认数据或重试请求。 ‌
核心差异:
触发时机‌:onStatus在响应阶段触发,onErrorResume在请求阶段触发。
‌处理范围‌:onStatus仅处理HTTP状态码异常,onErrorResume处理所有请求异常(包括网络问题、超时等)。
‌链式操作‌:onErrorResume支持链式调用(如重试、返回默认值等),onStatus通常仅用于分支处理。 ‌
 

    // 处理错误响应
    public Mono<JSONObject> getUserWithErrorHandling(Long id) {
        return webClient.get()
                .uri("/users/{id}", id)
                .retrieve()
                .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("客户端错误")))
                .onStatus(HttpStatusCode::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("服务器错误")))
                .bodyToMono(JSONObject.class);
    }
        Mono<ResponseEntity<String>> mono = WebClient.create().get().uri("http://")
                .retrieve()
                .toEntity(String.class)
                .onErrorResume(WebClientResponseException.class, e -> {
                    if (e.getStatusCode().is4xxClientError()) {
                        return Mono.error(new HttpClientErrorException(e.getStatusCode(), e.getResponseBodyAsString()));
                    }
                    return Mono.error(e);
                });

7.OpenFeign或者Feign

Feign 是 Spring Cloud 提供的一个声明式的 HTTP 客户端工具,它基于注解和接口定义的方式,简化了外部接口调用的流程。Feign 集成了 Ribbon 负载均衡和 Hystrix 熔断器等功能,使得接口调用更加灵活可靠。

@FeignClient(name = "user-service", url = "http://api.example.com")
public interface UserFeignClient {
   
   

    @GetMapping("/user/{id}")
    User getUser(@PathVariable("id") String id);
}

总结:

1.WebClient与RestTemplate对比

特性

WebClient

RestTemplate

编程模型

  • WebClient 是 Spring WebFlux 框架引入的非阻塞响应式 Web 客户端。
  • 在等待响应返回时不会阻塞正在执行的线程。只有当响应就绪时,才会产生通知。
  • WebClient 非常方便地处理并发,减少了样板代码。
  • RestTemplate 使用 Java Servlet API,是同步和阻塞的方法。
  • RestTemplate 需要显式管理线程,增加了复杂性。

性能

更好

一般

资源利用

更高效

一般

学习曲线

较陡

平缓

适用场景

高并发、响应式系统

简单应用、传统系统

参考

掌握 Spring 中的 WebClient-腾讯云开发者社区-腾讯云

重学SpringBoot3-WebClient配置与使用详解-腾讯云开发者社区-腾讯云

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值