調用 REST API 獲取 JSON 數據是微服務架構常見需求。HTTPS 需處理 SSL 證書信任、主機名驗證;HTTP 需處理連接池、超時、重試。本文提供生產級原生實現方案。
public class HttpClientUtil {
private static final int CONNECT_TIMEOUT = 5000;
private static final int READ_TIMEOUT = 10000;
public static JSONObject httpRequest(String url, String method) throws IOException {
return httpRequest(url, method, null);
}
public static JSONObject httpRequest(String url, String method, String body) throws IOException {
HttpURLConnection conn = createConnection(url);
conn.setRequestMethod(method);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setRequestProperty("Accept", "application/json");
if (body != null) {
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
try (OutputStream os = conn.getOutputStream()) {
os.write(body.getBytes(StandardCharsets.UTF_8));
}
}
return parseResponse(conn);
}
private static HttpURLConnection createConnection(String urlString) throws IOException {
URL url = new URL(urlString);
return (HttpURLConnection) url.openConnection();
}
private static JSONObject parseResponse(HttpURLConnection conn) throws IOException {
int status = conn.getResponseCode();
InputStream is = status >= 400 ? conn.getErrorStream() : conn.getInputStream();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(is, StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) sb.append(line);
return new JSONObject(sb.toString());
} finally {
conn.disconnect();
}
}
}
public class HttpsClientUtil {
public static JSONObject httpsRequest(String url, String method, String body)
throws IOException, GeneralSecurityException {
// 1. 創建信任所有證書的 TrustManager
TrustManager[] trustAll = new TrustManager[]{
new X509ExtendedTrustManager() {
@Override public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}
};
// 2. 初始化 SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAll, new SecureRandom());
// 3. 創建連接並設置 SocketFactory
URL urlObj = new URL(url);
HttpsURLConnection conn = (HttpsURLConnection) urlObj.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
// 4. 禁用主機名驗證 (僅測試)
conn.setHostnameVerifier((hostname, session) -> true);
// 後續設置同 HTTP
conn.setRequestMethod(method);
conn.setConnectTimeout(5000);
conn.setReadTimeout(10000);
// ... 發送請求體、解析響應 (同上)
return parseResponse(conn);
}
}
// 正確做法:加載指定信任庫
KeyStore trustStore = KeyStore.getInstance("JKS");
try (InputStream is = new FileInputStream("truststore.jks")) {
trustStore.load(is, "changeit".toCharArray());
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
HostnameVerifier 校驗 CN/SAN| 庫 | 特點 | 適用場景 |
|---|---|---|
| Apache HttpClient 4.5+ | 連接池、重試、攔截器、完整 HTTP 協議支持 | 企業級應用、高併發 |
| OkHttp 3.x+ | 連接池、HTTP/2、透明 GZIP、現代 API | Android、微服務調用 |
| Spring RestTemplate / WebClient | Spring 生態集成、聲明式配置、響應式 | Spring Boot 應用 |
| JDK 11+ HttpClient | 原生、支持 HTTP/2、WebSocket、CompletableFuture | 無第三方依賴項目 |
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.header("Accept", "application/json")
.GET()
.build();
CompletableFuture<String> future = client.sendAsync(request, BodyHandlers.ofString())
.thenApply(HttpResponse::body);
String json = future.join(); // 或異步處理
JSONObject data = new JSONObject(json);
原生 HttpURLConnection 適合簡單場景,生產環境強烈建議使用成熟 HTTP 客戶端庫。HTTPS 核心在於正確配置信任庫與主機名驗證,切勿在生產代碼中禁用安全檢查。掌握 JDK 11+ 原生 HttpClient 可在零依賴下獲得現代異步 HTTP 能力。