2009-09-21 84 views
44

我有一個調用HTTPGET得到HTML輸出相當簡單HttpClient的4碼。 HTML返回的腳本和圖像位置全部設置爲本地(例如<img src="/images/foo.jpg"/>),所以我需要調用URL以將它們設置爲絕對(<img src="http://foo.com/images/foo.jpg"/>)現在問題出現了 - 在調用期間可能會有一兩個302重定向,因此原始URL是不再反映HTML的位置。HttpClient的4 - 如何捕捉最後重定向URL

我怎麼給所有的重定向我可能(或沒有)返回的內容的最新網址是什麼?

我看着HttpGet#getAllHeaders()HttpResponse#getAllHeaders() - 找不到任何東西。

編輯:HttpGet#getURI()返回原始調用地址

回答

60

這將是當前的URL,您可以通過調用

​​

編輯得到:你沒有提到你是怎麼做的重定向。這對我們很有用,因爲我們自己處理302。

聽起來你正在使用DefaultRedirectHandler。我們曾經這樣做過。獲取當前URL有點棘手。你需要使用你自己的上下文。以下是相關的代碼片段,

 HttpGet httpget = new HttpGet(url); 
     HttpContext context = new BasicHttpContext(); 
     HttpResponse response = httpClient.execute(httpget, context); 
     if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) 
      throw new IOException(response.getStatusLine().toString()); 
     HttpUriRequest currentReq = (HttpUriRequest) context.getAttribute( 
       ExecutionContext.HTTP_REQUEST); 
     HttpHost currentHost = (HttpHost) context.getAttribute( 
       ExecutionContext.HTTP_TARGET_HOST); 
     String currentUrl = (currentReq.getURI().isAbsolute()) ? currentReq.getURI().toString() : (currentHost.toURI() + currentReq.getURI()); 

默認重定向並沒有爲我們工作,所以我們改變了,但我忘了是什麼問題。

+0

唉,它不會 - getURI()返回原始調用URL – Bostone 2009-09-21 22:17:15

+0

請參閱我的編輯.................. – 2009-09-22 00:55:23

+1

我不做任何特別的事情 - 非常基本HttpGet代碼。我谷歌我的問題我想我需要禁用自動重定向和「跟蹤線索」,直到我得到200 – Bostone 2009-09-22 01:19:59

2

在2.3版的Android仍然不支持以下重定向(HTTP代碼302)。我剛剛閱讀位置標題並再次下載:

if (statusCode != HttpStatus.SC_OK) { 
    Header[] headers = response.getHeaders("Location"); 

    if (headers != null && headers.length != 0) { 
     String newUrl = headers[headers.length - 1].getValue(); 
     // call again the same downloading method with new URL 
     return downloadBitmap(newUrl); 
    } else { 
     return null; 
    } 
} 

此處沒有循環重定向保護,所以要小心。更多關於通過博客Follow 302 redirects with AndroidHttpClient

4

我認爲更容易找到一個網址的方式是使用DefaultRedirectHandler。

package ru.test.test; 

import java.net.URI; 

import org.apache.http.HttpResponse; 
import org.apache.http.ProtocolException; 
import org.apache.http.impl.client.DefaultRedirectHandler; 
import org.apache.http.protocol.HttpContext; 

public class MyRedirectHandler extends DefaultRedirectHandler { 

    public URI lastRedirectedUri; 

    @Override 
    public boolean isRedirectRequested(HttpResponse response, HttpContext context) { 

     return super.isRedirectRequested(response, context); 
    } 

    @Override 
    public URI getLocationURI(HttpResponse response, HttpContext context) 
      throws ProtocolException { 

     lastRedirectedUri = super.getLocationURI(response, context); 

     return lastRedirectedUri; 
    } 

} 

代碼使用這個處理程序:

DefaultHttpClient httpclient = new DefaultHttpClient(); 
    MyRedirectHandler handler = new MyRedirectHandler(); 
    httpclient.setRedirectHandler(handler); 

    HttpGet get = new HttpGet(url); 

    HttpResponse response = httpclient.execute(get); 

    HttpEntity entity = response.getEntity(); 
    lastUrl = url; 
    if(handler.lastRedirectedUri != null){ 
     lastUrl = handler.lastRedirectedUri.toString(); 
    } 
+0

最新版本的HttpClient中不推薦使用HttpClient#setRedirectHandler()方法。 – 2012-08-24 02:25:01

+0

有誰知道如何在最新版本中處理這個問題? – 2016-08-08 17:34:55

5

一個恕我直言,改進方法基於ZZ編碼器的解決方案是使用一個ResponseInterceptor簡單地跟蹤過去的重定向位置。這樣你就不會丟失信息,例如在標籤後面。沒有響應攔截器,你會失去hashtag。例如:http://j.mp/OxbI23

private static HttpClient createHttpClient() throws NoSuchAlgorithmException, KeyManagementException { 
    SSLContext sslContext = SSLContext.getInstance("SSL"); 
    TrustManager[] trustAllCerts = new TrustManager[] { new TrustAllTrustManager() }; 
    sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); 

    SSLSocketFactory sslSocketFactory = new SSLSocketFactory(sslContext); 
    SchemeRegistry schemeRegistry = new SchemeRegistry(); 
    schemeRegistry.register(new Scheme("https", 443, sslSocketFactory)); 
    schemeRegistry.register(new Scheme("http", 80, new PlainSocketFactory())); 

    HttpParams params = new BasicHttpParams(); 
    ClientConnectionManager cm = new org.apache.http.impl.conn.SingleClientConnManager(schemeRegistry); 

    // some pages require a user agent 
    AbstractHttpClient httpClient = new DefaultHttpClient(cm, params); 
    HttpProtocolParams.setUserAgent(httpClient.getParams(), "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0.1"); 

    httpClient.setRedirectStrategy(new RedirectStrategy()); 

    httpClient.addResponseInterceptor(new HttpResponseInterceptor() { 
     @Override 
     public void process(HttpResponse response, HttpContext context) 
       throws HttpException, IOException { 
      if (response.containsHeader("Location")) { 
       Header[] locations = response.getHeaders("Location"); 
       if (locations.length > 0) 
        context.setAttribute(LAST_REDIRECT_URL, locations[0].getValue()); 
      } 
     } 
    }); 

    return httpClient; 
} 

private String getUrlAfterRedirects(HttpContext context) { 
    String lastRedirectUrl = (String) context.getAttribute(LAST_REDIRECT_URL); 
    if (lastRedirectUrl != null) 
     return lastRedirectUrl; 
    else { 
     HttpUriRequest currentReq = (HttpUriRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST); 
     HttpHost currentHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); 
     String currentUrl = (currentReq.getURI().isAbsolute()) ? currentReq.getURI().toString() : (currentHost.toURI() + currentReq.getURI()); 
     return currentUrl; 
    } 
} 

public static final String LAST_REDIRECT_URL = "last_redirect_url"; 

使用它,就像ZZ編碼器的解決方案:

HttpResponse response = httpClient.execute(httpGet, context); 
String url = getUrlAfterRedirects(context); 
0

這是我設法得到重定向URL:

Header[] arr = httpResponse.getHeaders("Location"); 
for (Header head : arr){ 
    String whatever = arr.getValue(); 
} 

或者,如果你確信只有一個重定向位置,請執行此操作:

httpResponse.getFirstHeader("Location").getValue(); 
+2

這對我不起作用。它僅返回最後一個請求的標題。 – 2013-07-18 21:39:20

26

在HttpClient的4,如果使用的是LaxRedirectStrategyDefaultRedirectStrategy任何亞類,這是推薦的方法(見的DefaultRedirectStrategy源代碼):

HttpContext context = new BasicHttpContext(); 
HttpResult<T> result = client.execute(request, handler, context); 
URI finalUrl = request.getURI(); 
RedirectLocations locations = (RedirectLocations) context.getAttribute(DefaultRedirectStrategy.REDIRECT_LOCATIONS); 
if (locations != null) { 
    finalUrl = locations.getAll().get(locations.getAll().size() - 1); 
} 

由於HttpClient的版本4.3.x,上面的代碼可以簡化爲:

HttpClientContext context = HttpClientContext.create(); 
HttpResult<T> result = client.execute(request, handler, context); 
URI finalUrl = request.getURI(); 
List<URI> locations = context.getRedirectLocations(); 
if (locations != null) { 
    finalUrl = locations.get(locations.size() - 1); 
} 
+3

您的答案應該已收到複選標記。這就是Apache如何實現這個目標!做得好! – Martijn 2014-09-11 18:53:15

+1

簡單而簡單。這個解決方案比這裏提到的所有其他解決方案都更好 – korpe 2015-05-08 12:33:22

+0

我收到的迴應有狀態代碼204,這意味着沒有內容。但是,響應中有一個位置標題。但在這種情況下Apache HttpClient沒有獲取位置標題。我認爲是因爲204的迴應。有沒有解決的辦法? – Arya 2016-10-09 15:30:44

9
HttpGet httpGet = new HttpHead("<put your URL here>"); 
    HttpClient httpClient = HttpClients.createDefault(); 
    HttpClientContext context = HttpClientContext.create(); 
    httpClient.execute(httpGet, context); 
    List<URI> redirectURIs = context.getRedirectLocations(); 
    if (redirectURIs != null && !redirectURIs.isEmpty()) { 
     for (URI redirectURI : redirectURIs) { 
      System.out.println("Redirect URI: " + redirectURI); 
     } 
     URI finalURI = redirectURIs.get(redirectURIs.size() - 1); 
    } 
+1

其他需要注意的(帶有所有這些答案)是「[原子HTTP重定向處理](https://fetch.spec.whatwg.org/#atomic-http-redirect-handling)」的概念,這表明出於安全目的,客戶端(至少某些類型的網絡應用程序)不應該能夠看到除最後一個重定向URL之外的任何內容。 (但是,在Java中可能很難完全防止它)。 – 2015-10-02 12:19:56

3

我發現這對HttpComponents Client Documentation

CloseableHttpClient httpclient = HttpClients.createDefault(); 
HttpClientContext context = HttpClientContext.create(); 
HttpGet httpget = new HttpGet("http://localhost:8080/"); 
CloseableHttpResponse response = httpclient.execute(httpget, context); 
try { 
    HttpHost target = context.getTargetHost(); 
    List<URI> redirectLocations = context.getRedirectLocations(); 
    URI location = URIUtils.resolve(httpget.getURI(), target, redirectLocations); 
    System.out.println("Final HTTP location: " + location.toASCIIString()); 
    // Expected to be an absolute URI 
} finally { 
    response.close(); 
}