首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

HttpClient 重定向後獲取最終 URL:HttpContext 捕獲目標主機與實際請求 URI

     編輯:Java 程式設計入門

前言:自動重定向下的 URL 追蹤難題

Apache HttpClient **預設自動處理重定向**(301/302/303/307/308),最終回傳的是**重定向鏈末端的響應內容**。但業務常需知道:**最終落地的真實 URL 是什麼?** 本文示範如何利用 HttpContext 在執行鏈中捕獲 HttpHost(目標主機)與 HttpUriRequest(實際請求 URI)。

核心原理

  • HttpContext 是 HttpClient **跨請求/響應交互的狀態容器**。
  • 執行 execute(HttpUriRequest, HttpContext) 時,HttpClient 將**重定向過程中的狀態**寫入 Context。
  • 關鍵屬性鍵(org.apache.http.protocol.ExecutionContext):
    • HTTP_TARGET_HOST → HttpHost(最終目標主機:scheme + host + port)
    • HTTP_REQUEST → HttpUriRequest(實際發出的請求,含最終 URI)

完整代碼範例

import org.apache.http.*;
import org.apache.http.client.*;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.*;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.*;
import org.apache.http.protocol.*;
import org.apache.http.util.EntityUtils;

public class HttpClientRedirectDemo {

    public static void getRedirectInfo() {
        // 1. 建立 HttpClient (4.5+ 建議用 HttpClients.createDefault())
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // 2. 建立 HttpContext 攜帶狀態
            HttpContext httpContext = new BasicHttpContext();

            // 3. 發起請求(假設此 URL 會重定向)
            HttpGet httpGet = new HttpGet("https://127.0.0.1:8088/blog/main.jsp");

            // 4. 執行請求,攜帶 Context
            try (CloseableHttpResponse response = httpClient.execute(httpGet, httpContext)) {

                // 5. 從 Context 取得重定向後的資訊
                HttpHost targetHost = (HttpHost) httpContext.getAttribute(HttpClientContext.TARGET_HOST);
                HttpUriRequest realRequest = (HttpUriRequest) httpContext.getAttribute(HttpClientContext.REQUEST);

                System.out.println("最終目標主機: " + targetHost);           // 例: https://127.0.0.1:8088
                System.out.println("最終請求 URI: " + realRequest.getURI()); // 例: /blog/admin/login.jsp

                // 6. 處理響應實體
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String charset = ContentType.getOrDefault(entity).getCharset();
                    System.out.println("響應內容: " + EntityUtils.toString(entity, charset));
                    EntityUtils.consume(entity); // 釋放連線
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        getRedirectInfo();
    }
}

關鍵 API 對應表 (HttpClient 4.5+)

舊常數 (ExecutionContext)新常數 (HttpClientContext)類型說明
ExecutionContext.HTTP_TARGET_HOSTHttpClientContext.TARGET_HOSTHttpHost最終目標主機
ExecutionContext.HTTP_REQUESTHttpClientContext.REQUESTHttpUriRequest實際發出的請求
ExecutionContext.HTTP_RESPONSEHttpClientContext.RESPONSEHttpResponse最終響應
ExecutionContext.HTTP_TARGET_AUTH_STATEHttpClientContext.TARGET_AUTH_STATEAuthState目標認證狀態
ExecutionContext.HTTP_PROXY_AUTH_STATEHttpClientContext.PROXY_AUTH_STATEAuthState代理認證狀態

進階場景:手動控制重定向

若需**攔截重定向**、記錄鏈路、或**禁止自動重定向**:

// 1. 禁用自動重定向
RequestConfig config = RequestConfig.custom()
        .setRedirectsEnabled(false)
        .build();
HttpGet get = new HttpGet(url);
get.setConfig(config);

// 2. 手動處理 302
try (CloseableHttpResponse resp = client.execute(get)) {
    int status = resp.getStatusLine().getStatusCode();
    if (status == 301 || status == 302 || status == 303 || status == 307 || status == 308) {
        Header location = resp.getFirstHeader("Location");
        String redirectUrl = location.getValue();
        System.out.println("重定向到: " + redirectUrl);
        // 可選:手動發起下一跳請求
    }
    EntityUtils.consume(resp.getEntity());
}

HttpClient 5.x (Java 11+) 現代化寫法

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.client5.http.protocol.HttpClientContext;

try (CloseableHttpClient client = HttpClients.createDefault()) {
    HttpClientContext context = HttpClientContext.create();
    HttpGet get = new HttpGet("https://example.com/redirect");
    try (ClassicHttpResponse resp = client.execute(get, context)) {
        HttpHost target = context.getTargetHost();
        HttpGet realRequest = (HttpGet) context.getRequest();
        System.out.println("Target: " + target);
        System.out.println("URI: " + realRequest.getUri());
    }
}

常見問題排查

現象原因解決
targetHost 為 null未發生重定向、或 Context 未正確傳遞確認 execute(req, context) 雙參數版本
最終 URI 為原始 URL伺服器未重定向、或重定向被禁用檢查伺服器回應碼、RequestConfig.setRedirectsEnabled(true)
HTTPS 重定向主機顯示 IPSNI/憑證驗證導致主機名解析差異屬正常行為,HttpHost 以實際連線目標為準
相對路徑 Location伺服器回應相對重定向HttpClient 內部自動解析為絕對 URI,realRequest.getURI() 已是絕對

總結

  • HttpClient 自動重定向**不阻礙獲取最終 URL**,關鍵在於**攜帶 HttpContext 執行請求**。
  • 從 Context 取得 HttpClientContext.TARGET_HOST (主機) 與 HttpClientContext.REQUEST (完整 URI)。
  • HttpClient 4.5+ 推薦使用 HttpClientContext 常數替代舊 ExecutionContext。
  • 需手動控制重定向時,關閉自動重定向並解析 Location 標頭。
  • HttpClient 5.x API 更現代化,同樣支援 Context 捕獲重定向資訊。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved