接口调用是现代软件开发中最基础、最核心的技能之一。本文将从最基础的 HTTP 请求讲起,逐步深入到生产级的接口调用方案,涵盖多种技术栈和实际场景。
一、基础篇:HTTP 请求的核心原理
1.1 HTTP 请求的本质
一个完整的 HTTP 请求包含以下要素:
请求行: GET /api/users/1 HTTP/1.1
请求头: Host: api.example.com
Content-Type: application/json
Authorization: Bearer xxx
请求体: {"name": "Alice"} (GET请求通常无请求体)
1.2 最基础的接口调用(原生 Java)
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.*;
public class BasicHttpClient {
public String sendGet(String urlString) throws Exception {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000); // 连接超时
conn.setReadTimeout(5000); // 读取超时
int responseCode = conn.getResponseCode();
System.out.println("Response Code: " + responseCode);
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream())
);
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
return response.toString();
}
public String sendPost(String urlString, String jsonBody) throws Exception {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true); // 允许写入请求体
// 发送请求体
try (OutputStream os = conn.getOutputStream()) {
byte[] input = jsonBody.getBytes("utf-8");
os.write(input, 0, input.length);
}
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "utf-8")
);
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line.trim());
}
return response.toString();
}
}
注意:原生 HttpURLConnection 虽然零依赖,但代码冗长、功能有限,生产环境建议使用成熟的 HTTP 客户端库。
二、进阶篇:使用成熟 HTTP 客户端
2.1 Apache HttpClient(Java 经典方案)
Maven 依赖:
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
封装工具类:
java复制
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
public class ApacheHttpClientUtil {
private static final CloseableHttpClient httpClient = HttpClients.custom()
.setMaxConnTotal(200) // 最大连接数
.setMaxConnPerRoute(50) // 每个路由最大连接数
.build();
/**
* GET 请求
*/
public static String doGet(String url) {
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader("Accept", "application/json");
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
return EntityUtils.toString(response.getEntity());
} catch (Exception e) {
throw new RuntimeException("GET请求失败: " + url, e);
}
}
/**
* POST 请求(JSON)
*/
public static String doPost(String url, String jsonBody) {
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-Type", "application/json");
httpPost.setEntity(new StringEntity(jsonBody));
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
return EntityUtils.toString(response.getEntity());
} catch (Exception e) {
throw new RuntimeException("POST请求失败: " + url, e);
}
}
}
2.2 OkHttp(轻量高效,Android 首选)
Maven 依赖:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
同步调用示例:
import okhttp3.*;
import java.io.IOException;
public class OkHttpDemo {
private static final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
.build();
private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
// GET 请求
public String get(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.header("User-Agent", "MyApp/1.0")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code: " + response);
}
return response.body().string();
}
}
// POST 请求
public String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
}
异步调用示例(非阻塞):
// 异步 GET,不阻塞主线程
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody responseBody = response.body()) {
System.out.println(responseBody.string());
}
}
});
三、实战篇:Spring 生态中的接口调用
3.1 RestTemplate(Spring 经典方案)
import org.springframework.web.client.RestTemplate;
import org.springframework.http.*;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(5000);
factory.setReadTimeout(10000);
return new RestTemplate(factory);
}
}
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
public User getUserById(Long id) {
String url = "https://api.example.com/users/{id}";
// 路径参数 + 返回值自动映射
return restTemplate.getForObject(url, User.class, id);
}
public User createUser(User user) {
String url = "https://api.example.com/users";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<User> request = new HttpEntity<>(user, headers);
ResponseEntity<User> response = restTemplate.postForEntity(url, request, User.class);
return response.getBody();
}
// 带请求头的复杂调用
public List<Order> getOrders(String token) {
String url = "https://api.example.com/orders";
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(token); // Bearer Token
headers.set("X-Request-Id", UUID.randomUUID().toString());
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<Order[]> response = restTemplate.exchange(
url,
HttpMethod.GET,
entity,
Order[].class
);
return Arrays.asList(response.getBody());
}
}
3.2 WebClient(响应式,Spring 5+ 推荐)
Maven 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
基础使用:
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Service
public class WebClientService {
private final WebClient webClient;
public WebClientService(WebClient.Builder builder) {
this.webClient = builder
.baseUrl("https://api.example.com")
.defaultHeader("Content-Type", "application/json")
.build();
}
// 同步调用(阻塞)
public User getUserSync(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.retrieve()
.bodyToMono(User.class)
.block(); // 阻塞等待结果
}
// 异步调用(非阻塞,推荐)
public Mono<User> getUserAsync(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.retrieve()
.bodyToMono(User.class);
}
// POST 请求 + 错误处理
public Mono<Order> createOrder(OrderRequest request) {
return webClient.post()
.uri("/orders")
.bodyValue(request)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError,
response -> Mono.error(new BusinessException("客户端错误")))
.onStatus(HttpStatusCode::is5xxServerError,
response -> Mono.error(new SystemException("服务端错误")))
.bodyToMono(Order.class);
}
}
配合 Spring MVC 使用(Controller 层):
java复制
@RestController
public class OrderController {
@Autowired
private WebClientService webClientService;
@GetMapping("/orders/{id}")
public Mono<Order> getOrder(@PathVariable Long id) {
// 全程非阻塞,线程不会被占用
return webClientService.getOrderAsync(id);
}
}
四、生产级篇:接口调用框架封装
4.1 统一封装:带重试、超时、日志的 HTTP 工具
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResourceAccessException;
@Slf4j
@Component
public class HttpClientWrapper {
@Autowired
private RestTemplate restTemplate;
/**
* 带重试的 GET 请求
* 遇到 ResourceAccessException(连接超时、读取超时)时自动重试
*/
@Retryable(
retryFor = {ResourceAccessException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2) // 1s, 2s, 4s
)
public <T> T getWithRetry(String url, Class<T> responseType) {
log.info("发起请求: {}", url);
long start = System.currentTimeMillis();
try {
T result = restTemplate.getForObject(url, responseType);
log.info("请求成功, 耗时{}ms", System.currentTimeMillis() - start);
return result;
} catch (Exception e) {
log.error("请求失败: {}, 异常: {}", url, e.getMessage());
throw e;
}
}
}
启用重试(Spring Boot):
java复制
@EnableRetry
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4.2 OpenFeign:声明式 HTTP 客户端(微服务首选)
Maven 依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
定义接口(像调用本地方法一样调用远程接口):
java复制
@FeignClient(
name = "user-service",
url = "https://api.example.com",
configuration = FeignConfig.class,
fallbackFactory = UserClientFallbackFactory.class
)
public interface UserClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
@PostMapping("/users")
User createUser(@RequestBody User user);
@GetMapping("/users")
List<User> getUsersByIds(@RequestParam("ids") List<Long> ids);
}
配置类:
java复制
public class FeignConfig {
@Bean
public Request.Options feignOptions() {
// 连接超时 5s,读取超时 10s
return new Request.Options(5, TimeUnit.SECONDS, 10, TimeUnit.SECONDS, true);
}
@Bean
public Retryer feignRetryer() {
// 初始间隔 100ms,最大间隔 1s,最多重试 3 次(不含首次)
return new Retryer.Default(100, 1000, 3);
}
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL; // 打印完整请求/响应
}
}
降级处理(熔断时返回兜底数据):
@Component
@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
@Override
public UserClient create(Throwable cause) {
log.error("UserClient 调用失败: {}", cause.getMessage());
return new UserClient() {
@Override
public User getUserById(Long id) {
// 返回兜底用户
return User.builder()
.id(id)
.name("未知用户")
.status("OFFLINE")
.build();
}
@Override
public User createUser(User user) {
throw new BusinessException("用户服务暂不可用,请稍后重试");
}
@Override
public List<User> getUsersByIds(List<Long> ids) {
return Collections.emptyList();
}
};
}
}
启用 Feign:
java复制
@EnableFeignClients
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
使用示例:
java复制
@Service
public class OrderService {
@Autowired
private UserClient userClient;
public OrderDetail getOrderDetail(Long orderId) {
Order order = orderRepository.findById(orderId);
// 像调用本地方法一样调用远程接口
User user = userClient.getUserById(order.getUserId());
return OrderDetail.builder()
.order(order)
.user(user)
.build();
}
}
五、Python 中的接口调用
5.1 requests 库(最常用)
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# 创建带重试策略的 Session
session = requests.Session()
# 配置重试:连接错误重试3次,读取超时重试3次,状态码 500/502/503/504 重试3次
retries = Retry(
total=3,
backoff_factor=1, # 间隔 0s, 2s, 4s
status_forcelist=[500, 502, 503, 504],
allowed_methods=["HEAD", "GET", "POST", "PUT", "DELETE"]
)
session.mount('https://', HTTPAdapter(max_retries=retries))
session.mount('http://', HTTPAdapter(max_retries=retries))
# GET 请求
def get_user(user_id: int) -> dict:
url = f"https://api.example.com/users/{user_id}"
response = session.get(url, timeout=(5, 10)) # (连接超时, 读取超时)
response.raise_for_status() # 状态码 >= 400 时抛出异常
return response.json()
# POST 请求
def create_user(user_data: dict) -> dict:
url = "https://api.example.com/users"
headers = {"Content-Type": "application/json"}
response = session.post(url, json=user_data, headers=headers, timeout=10)
return response.json()
# 文件上传
def upload_file(file_path: str) -> dict:
url = "https://api.example.com/upload"
with open(file_path, 'rb') as f:
files = {'file': ('report.pdf', f, 'application/pdf')}
response = session.post(url, files=files)
return response.json()
5.2 aiohttp(异步高性能)
import aiohttp
import asyncio
async def fetch_user(session: aiohttp.ClientSession, user_id: int) -> dict:
url = f"https://api.example.com/users/{user_id}"
async with session.get(url) as response:
response.raise_for_status()
return await response.json()
async def main():
# 创建连接池,限制并发数
connector = aiohttp.TCPConnector(limit=100, limit_per_host=30)
async with aiohttp.ClientSession(
connector=connector,
timeout=aiohttp.ClientTimeout(total=30)
) as session:
# 并发请求 10 个用户
tasks = [fetch_user(session, i) for i in range(1, 11)]
results = await asyncio.gather(*tasks, return_exceptions=True)
for result in results:
if isinstance(result, Exception):
print(f"请求失败: {result}")
else:
print(f"用户: {result}")
asyncio.run(main())
六、核心要点总结
| 维度 | 建议 |
|---|---|
| 超时设置 | 必须设置连接超时和读取超时,避免无限等待 |
| 连接池 | 复用连接,减少 TCP 握手开销 |
| 重试策略 | 仅对幂等操作(GET、PUT)重试,POST 需谨慎 |
| 异常处理 | 区分网络异常、超时异常、业务异常 |
| 日志记录 | 记录请求 URL、耗时、状态码,便于排查问题 |
| 序列化 | 使用 Jackson/Gson 自动处理 JSON 与对象的映射 |
| 安全 | HTTPS 必用,敏感信息放 Header 而非 URL |
七、选型建议
| 场景 | 推荐方案 |
|---|---|
| 简单脚本/爬虫 | Python requests |
| Android 开发 | OkHttp |
| Spring Boot 单体应用 | RestTemplate / WebClient |
| Spring Cloud 微服务 | OpenFeign + Ribbon |
| 高并发异步场景 | WebClient / aiohttp |
| 需要精细控制 | Apache HttpClient / OkHttp接口调用的代码实现看似简单,但要做到稳定、高效、可维护,需要在超时控制、重试策略、连接管理、异常处理等方面下足功夫。希望本文能为你的接口调用实践提供有价值的参考。 |
如遇任何疑问或有进一步的需求,请随时与我私信或者评论联系。

