J'ai un code HttpClient 4 assez simple qui appelle HttpGet pour obtenir une sortie HTML. Le code HTML revient avec des scripts et des emplacements d'images tous définis en local (par exemple <img src="/images/foo.jpg"/>
). J'ai donc besoin d'appeler l'URL pour les transformer en absolus (<img src="http://foo.com/images/foo.jpg"/>
). Maintenant vient le problème - pendant l'appel, il peut y avoir une ou deux redirections 302 afin que l'URL d'origine ne reflète plus l'emplacement du HTML.
Comment puis-je obtenir la dernière URL du contenu renvoyé compte tenu de toutes les redirections que je peux (ou pas) avoir?
J'ai regardé HttpGet#getAllHeaders()
et HttpResponse#getAllHeaders()
- je n'ai rien trouvé.
Modifié: HttpGet#getURI()
renvoie l'adresse d'appel d'origine
Ce serait l'URL actuelle, que vous pouvez obtenir en appelant
HttpGet#getURI();
EDIT: Vous n'avez pas mentionné comment vous faites la redirection. Cela fonctionne pour nous car nous gérons nous-mêmes le 302.
On dirait que vous utilisez DefaultRedirectHandler. Nous le faisions. Il est assez difficile d'obtenir l'URL actuelle. Vous devez utiliser votre propre contexte. Voici les extraits de code pertinents,
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 redirection par défaut ne fonctionnait pas pour nous, nous avons donc changé mais j'ai oublié quel était le problème.
Dans HttpClient 4, si vous utilisez LaxRedirectStrategy
ou toute sous-classe de DefaultRedirectStrategy
, c'est la méthode recommandée (voir le code source 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);
}
Depuis HttpClient 4.3.x, le code ci-dessus peut être simplifié comme suit:
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);
}
Une manière améliorée à mon humble avis basée sur la solution de ZZ Coder consiste à utiliser un ResponseInterceptor pour simplement suivre le dernier emplacement de redirection. De cette façon, vous ne perdez pas d'informations, par exemple après un hashtag. Sans l'intercepteur de réponse, vous perdez le hashtag. Exemple: http://j.mp/OxbI2
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";
utilisez-le comme la solution de ZZ Coder:
HttpResponse response = httpClient.execute(httpGet, context);
String url = getUrlAfterRedirects(context);
J'ai trouvé cela sur Documentation client 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();
}
Je pense que le moyen le plus simple de trouver la dernière URL est d'utiliser 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;
}
}
Code pour utiliser ce gestionnaire:
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();
}
Dans la version 2.3 Android ne prend toujours pas en charge la redirection suivante (code HTTP 302). Je viens de lire l'en-tête de l'emplacement et de télécharger à nouveau:
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;
}
}
Aucune protection contre les redirections circulaires ici, alors soyez prudent. Plus sur par blog Suivez les redirections 302 avec AndroidHttpClient
Voici comment j'ai réussi à obtenir l'URL de redirection:
Header[] arr = httpResponse.getHeaders("Location");
for (Header head : arr){
String whatever = arr.getValue();
}
Ou, si vous êtes sûr qu'il n'y a qu'un seul emplacement de redirection, procédez comme suit:
httpResponse.getFirstHeader("Location").getValue();