HttpClient 4 - como capturar última URL de redirecionamento
-
12-09-2019 - |
Pergunta
Eu tenho bastante simples código HttpClient 4 que chama HttpGet para obter HTML de saída. Os retornos HTML com scripts e locais de imagem tudo pronto para locais (por exemplo <img src="/images/foo.jpg"/>
) Então eu preciso chamando URL para fazer isso em absoluto (<img src="http://foo.com/images/foo.jpg"/>
) Agora vem o problema - durante a chamada pode haver um ou dois 302 redirecionamentos para que o URL original é já não reflecte a localização de HTML.
Como faço para receber as últimas URL do conteúdo retornado dado todos os redirecionamentos que podem (ou não podem) ter?
Eu olhei para HttpGet#getAllHeaders()
e HttpResponse#getAllHeaders()
- não consegui encontrar nada
Editado: HttpGet#getURI()
retorna endereço chamado original
Solução
Essa seria a URL atual, que você pode obter pelo telefone
HttpGet#getURI();
EDIT: Você não mencionou como você está fazendo redirecionamento. Que funciona para nós, porque lidar com a 302 nós mesmos.
Parece que você está usando DefaultRedirectHandler. Nós costumávamos fazer isso. É uma espécie de complicado para obter a URL atual. Você precisa usar o seu próprio contexto. Aqui estão os trechos 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());
O redirecionamento padrão não funcionou para nós para que mudou, mas eu esqueci qual era o problema.
Outras dicas
Em HttpClient 4, se você estiver usando LaxRedirectStrategy
ou qualquer subclasse de DefaultRedirectStrategy
, esta é a maneira recomendada (ver código-fonte do 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, o código acima pode ser simplificada 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);
}
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);
}
Um IMHO melhorou maneira baseada na solução de ZZ Coder é usar um ResponseInterceptor simplesmente acompanhar o último local de redirecionamento. Dessa forma, você fazer informação não perdem por exemplo após uma hashtag. Sem o interceptor resposta você perde a hashtag. Exemplo: 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";
usá-lo apenas como solução de ZZ Coder:
HttpResponse response = httpClient.execute(httpGet, context);
String url = getUrlAfterRedirects(context);
Eu achei essa mensagem em HttpComponents Documentação do Cliente
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();
}
Eu acho mais fácil maneira de encontrar última URL é 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;
}
}
O código para usar esse manipulador:
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();
}
Na versão 2.3 Android ainda não suportam seguinte redirecionamento HTTP (código 302). Acabei de ler cabeçalho de localização e download novamente:
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;
}
}
Sem proteção redirecionamentos circulares aqui por isso tome cuidado. Mais sobre por blogue Siga 302 redirecionamentos com AndroidHttpClient
Isto é como eu consegui obter o URL de redirecionamento:
Header[] arr = httpResponse.getHeaders("Location");
for (Header head : arr){
String whatever = arr.getValue();
}
Ou, se tiver certeza de que há apenas um local de redirecionamento, faça o seguinte:
httpResponse.getFirstHeader("Location").getValue();