2009-09-21 13 views
44

Tengo un código HttpClient 4 bastante simple que llama a HttpGet para obtener una salida de HTML. El HTML vuelve con scripts y ubicaciones de imágenes configuradas como locales (por ejemplo, <img src="/images/foo.jpg"/>), así que necesito llamar a URL para convertirlas en absolutas (<img src="http://foo.com/images/foo.jpg"/>) Ahora viene el problema: durante la llamada puede haber uno o dos redireccionamientos 302 por lo que la URL original es ya no refleja la ubicación de HTML.HttpClient 4: cómo capturar la última URL de redirección

¿Cómo obtengo la última URL del contenido devuelto con todas las redirecciones que puedo (o no) tener?

Miré HttpGet#getAllHeaders() y HttpResponse#getAllHeaders() - no pude encontrar nada.

Editado: HttpGet#getURI() dirección regresa vocación original de

Respuesta

60

que sería la URL actual, que se puede obtener llamando

HttpGet#getURI(); 

EDIT: Usted no ha mencionado cómo se están haciendo redirección. Eso funciona para nosotros porque manejamos el 302 nosotros mismos.

Parece que está utilizando DefaultRedirectHandler. Solíamos hacer eso. Es un poco complicado obtener la URL actual. Necesita usar su propio contexto. Estos son los fragmentos de código pertinentes,

 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()); 

La redirección predeterminado no funcionaba para nosotros así que cambiamos pero se me olvidó cuál era el problema.

+0

Desafortunadamente, getURI() me devuelve la URL de llamada original – Bostone

+0

Ver mi edición .................. –

+1

No hago nada especial - muy básico HttpGet code. Google mi problema, creo que necesito deshabilitar la redirección automática y "seguir el rastro" hasta que obtenga 200 – Bostone

2

En la versión 2.3 Android aún no es compatible con la siguiente redirección (código HTTP 302). Acabo de leer el encabezado de la ubicación y volver a descargar:

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; 
    } 
} 

Sin protección de redirecciones aquí, así que tenga cuidado. Más información por blog Follow 302 redirects with AndroidHttpClient

4

Creo que la manera más fácil de encontrar la última URL es usar 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; 
    } 

} 

código para utilizar este controlador:

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

El método HttpClient # setRedirectHandler() está en desuso en las últimas versiones de HttpClient. –

+0

¿Alguien sabe cómo manejar esto en la última versión? –

5

Un En mi humilde opinión mejorado basado camino sobre la solución de ZZ Coder es utilizar un ResponseInterceptor simplemente realizar un seguimiento de la última ubicación de redirección. De esta forma, no pierde información, p. después de un hashtag. Sin el interceptor de respuesta, pierdes el hashtag.Ejemplo: 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"; 

lo utilizan como solución de ZZ Coder:

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

Así es como me las arreglé para obtener el URL de redireccionamiento:

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

O, si está seguro de que solo hay una ubicación de redirección, haga esto:

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

Esto no funciona para mí. Devuelve solo los encabezados de la última solicitud. –

26

En HttpClient 4, si está utilizando LaxRedirectStrategy o cualquier subclase de DefaultRedirectStrategy, esta es la forma recomendada (véase el código fuente de 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); 
} 

Desde HttpClient 4.3.x, el código anterior se puede simplificar :

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

Su respuesta debería haber recibido la marca de verificación. ¡Así es como Apache realmente pensó esto! ¡Gran trabajo! – Martijn

+1

sencillo y simple. ¡Y esta solución funciona mejor que todas las demás mencionadas aquí! – korpe

+0

La respuesta que obtengo tiene el código de estado 204 que significa que no hay contenido. Sin embargo, hay un encabezado de ubicación en la respuesta. Pero Apache HttpClient no obtiene el encabezado de ubicación en este caso. Creo que debido a la 204 respuesta. ¿Hay alguna forma de evitar esto? – Arya

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

Algo más que hay que tener en cuenta (con todas estas respuestas) es el concepto de "[Manejo de redirección HTTP atómico] (https://fetch.spec.whatwg.org/#atomic-http-redirect-handling)", que sugiere que los clientes (al menos de algunos tipos, las aplicaciones web) no deberían poder ver ninguna, excepto la última de las URL de redireccionamiento, por razones de seguridad. (Sin embargo, en Java podría ser difícil prevenirlo por completo). –

3

he encontrado esto en 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(); 
} 
Cuestiones relacionadas