HTTP 客户端
Laravel 用
Http::get()/Http::post()(Guzzle 封装)发起外部请求;Spring Boot 3.x 的现代答案是RestClient(同步,Spring 6.1+ 推荐)和WebClient(响应式)。这篇主推RestClient,它最接近Http::的用法。
RestClient:最接近 Laravel Http 的写法
基础用法
java
@Service
public class WeatherService {
private final RestClient restClient = RestClient.create();
public String getWeather(String city) {
return restClient.get()
.uri("https://api.weather.com/v1/{city}", city) // 路径变量
.retrieve() // 发送并接收
.body(String.class); // 解析为 String
}
}对应 Laravel:
php
$response = Http::get("https://api.weather.com/v1/{$city}");
return $response->body();GET 带查询参数 + 解析 JSON
java
public Map<String, Object> search(String q, int page) {
return restClient.get()
.uri(uriBuilder -> uriBuilder
.path("/search")
.queryParam("q", q)
.queryParam("page", page)
.build())
.retrieve()
.body(Map.class);
}POST + JSON 请求体
java
public Map<String, Object> create(Map<String, Object> payload) {
return restClient.post()
.uri("https://api.example.com/posts")
.contentType(MediaType.APPLICATION_JSON)
.body(payload)
.retrieve()
.body(Map.class);
}TIP
RestClient 底层用 Jackson 自动把对象序列化为 JSON、把响应 JSON 反序列化为指定类型 —— 和 Laravel 里 Http::asJson()->post(...) 自动 JSON 化一致。
自定义请求头
java
restClient.post()
.uri(url)
.header("Authorization", "Bearer " + token)
.header("Accept-Language", "zh-CN")
...把接口封装成客户端类(推荐)
Laravel 社区会把第三方 API 封装成 Service;Java 也一样,还能把 RestClient 做成单例 Bean:
java
@Service
public class PaymentApiClient {
private final RestClient client;
public PaymentApiClient(RestClient.Builder builder) {
// 统一 Base URL、超时、日志,所有请求自动带上
this.client = builder
.baseUrl("https://api.payment.com/v2")
.defaultHeader("Authorization", "Bearer " + System.getenv("PAY_TOKEN"))
.build();
}
public Payment createOrder(Map<String, Object> order) {
return client.post()
.uri("/orders")
.body(order)
.retrieve()
.body(Payment.class); // 直接反序列化成 DTO
}
}什么时候用这种封装?
任何「外部服务」都建议封装成 Client 类,理由和 Laravel 封装第三方 API 一样:
- 接口变更只改一处
- 方便测试时 mock
- 统一超时/鉴权/日志
错误处理
Laravel Http::throw() 抛异常;Spring RestClient 默认非 2xx 会抛 RestClientResponseException:
java
try {
return client.get().uri(url).retrieve().body(Payment.class);
} catch (RestClientResponseException e) {
// e.getStatusCode()、e.getResponseBodyAsString()
log.warn("支付接口返回异常: {} {}", e.getStatusCode(), e.getResponseBodyAsString());
throw BusinessException.badRequest("支付服务暂时不可用");
}也可以在请求时自定义错误处理:
java
client.get()
.uri(url)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
(request, response) -> { throw new BusinessException(400, "请求参数问题"); })
.body(Payment.class);超时配置
java
RestClient client = RestClient.builder()
.requestFactory(new JdkClientHttpRequestFactory(HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5)) // 连接超时
.build()))
.baseUrl(url)
.build();WebClient:响应式场景才用
WebClient 是 Spring WebFlux 时代的产物,异步非阻塞。同一套写法能返回「异步结果」,适合高并发 I/O 密集场景:
java
@Service
public class AsyncWeatherService {
private final WebClient webClient = WebClient.create();
public Mono<String> getWeather(String city) {
return webClient.get()
.uri("https://api.weather.com/v1/{city}", city)
.retrieve()
.bodyToMono(String.class);
}
}怎么选
| 场景 | 用哪个 |
|---|---|
| 传统 MVC 项目同步调外部 API | RestClient(简单、直观) |
| 高并发异步、WebFlux 项目 | WebClient |
| 只想发个简单请求 | RestClient |
RestClient 内部是同步阻塞的,读起来和 Http:: 一模一样;WebClient 是响应式的,上手成本高。没有特殊理由选 RestClient。 |
对照速查
| 需求 | Laravel | RestClient |
|---|---|---|
| GET | Http::get($url) | client.get().uri(url).retrieve() |
| POST JSON | Http::asJson()->post($url, $data) | client.post().contentType(JSON).body(data) |
| 路径参数 | Http::get(".../{$id}") | .uri(url, id) |
| 查询参数 | Http::get($url, ['q' => $q]) | uriBuilder.queryParam("q", q) |
| 请求头 | Http::withHeaders([...]) | .header(name, value) |
| 抛错 | Http::throw() | 默认自动抛 RestClientResponseException |
| 超时 | Http::timeout(5) | 自定义 requestFactory |