java - example - HttpClient 4-cómo capturar la última URL de redireccionamiento
httppost java (8)
Así es como logré obtener la 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 redireccionamiento, haga lo siguiente:
httpResponse.getFirstHeader("Location").getValue();
Tengo un código HttpClient 4 bastante simple que llama a HttpGet para obtener resultados HTML. El HTML regresa con los scripts y las ubicaciones de las imágenes, todas configuradas como locales (por ejemplo, <img src="/images/foo.jpg"/>
), así que necesito llamar a la URL para convertirlas en absolutas ( <img src="http://foo.com/images/foo.jpg"/>
) Ahora surge el problema: durante la llamada puede haber uno o dos redireccionamientos 302, por lo que la URL original ya no refleja la ubicación del HTML.
¿Cómo obtengo la última URL del contenido devuelto dado todos los redireccionamientos que puedo (o no puedo) tener?
Miré HttpGet#getAllHeaders()
y HttpResponse#getAllHeaders()
- no pude encontrar nada.
Editado: HttpGet#getURI()
devuelve la dirección de llamada original
Creo que la forma 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();
}
En HttpClient 4, si está utilizando LaxRedirectStrategy
o cualquier subclase de DefaultRedirectStrategy
, esta es la forma recomendada (consulte 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 como:
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);
}
En la versión 2.3, Android aún no admite la siguiente redirección (código HTTP 302). Acabo de leer el encabezado de la ubicación y descargar de nuevo:
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;
}
}
Aquí no hay protecciones circulares, así que ten cuidado. Más información en el blog Siga 302 redirecciones con AndroidHttpClient
Encontré esto en la documentación del cliente HttpComponents
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();
}
Esa sería la URL actual, que puede obtener llamando
HttpGet#getURI();
EDIT: No mencionaste cómo estás redirigiendo. Eso nos funciona porque nosotros nos encargamos del 302.
Parece que está utilizando DefaultRedirectHandler. Solíamos hacer eso. Es un poco difícil obtener la URL actual. Necesitas usar tu propio contexto. Aquí están los fragmentos de código relevantes,
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 predeterminada no funcionó para nosotros, así que cambiamos, pero olvidé cuál era el problema.
Una forma mejorada IMHO basada en la solución de ZZ Coder es usar un ResponseInterceptor para simplemente rastrear la última ubicación de redireccionamiento. De esa manera no perderá información, por ejemplo, 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";
Úsalo como la solución de ZZ Coder:
HttpResponse response = httpClient.execute(httpGet, context);
String url = getUrlAfterRedirects(context);
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);
}