HttpClient 4 - как перехватить последний URL перенаправления
У меня есть довольно простой код HttpClient 4, который вызывает HttpGet для получения вывода HTML. HTML-код возвращается со сценариями и местоположениями изображений, настроенными как локальные (например, <img src="/images/foo.jpg"/>
), поэтому мне нужно вызвать URL, чтобы сделать их абсолютными (<img src="http://foo.com/images/foo.jpg"/>
) Теперь возникает проблема - во время вызова может быть один или два перенаправления 302, поэтому исходный URL больше не отражает местоположение HTML.
Как получить последний URL возвращенного контента с учетом всех перенаправлений, которые я могу (или не могу) иметь?
я смотрел на HttpGet#getAllHeaders()
а также HttpResponse#getAllHeaders()
- ничего не смог найти.
Отредактировано: HttpGet#getURI()
возвращает исходный телефонный адрес
8 ответов
Это будет текущий URL, который вы можете получить, позвонив
HttpGet#getURI();
РЕДАКТИРОВАТЬ: Вы не упомянули, как вы делаете перенаправление. Это работает для нас, потому что мы сами обрабатываем 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());
Перенаправление по умолчанию у нас не сработало, поэтому мы изменились, но я забыл, в чем проблема.
В HttpClient 4, если вы используете LaxRedirectStrategy
или любой подкласс DefaultRedirectStrategy
, это рекомендуемый способ (см. исходный код 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);
}
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);
}
Я нашел это в документации клиента 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();
}
IMHO улучшенный способ, основанный на решении ZZ Coder, состоит в том, чтобы использовать ResponseInterceptor, чтобы просто отслеживать последнее местоположение перенаправления. Таким образом, вы не потеряете информацию, например, после хэштега. Без перехватчика ответа вы теряете хэштег. Пример: 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 Coder:
HttpResponse response = httpClient.execute(httpGet, context);
String url = getUrlAfterRedirects(context);
Я думаю, что более простой способ найти последний URL - использовать 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();
}
В версии 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;
}
}
Здесь нет круговой защиты от перенаправлений, поэтому будьте осторожны. Подробнее в блоге Следуйте 302 перенаправления с AndroidHttpClient
Вот как мне удалось получить URL перенаправления:
Header[] arr = httpResponse.getHeaders("Location");
for (Header head : arr){
String whatever = arr.getValue();
}
Или, если вы уверены, что существует только одно место перенаправления, сделайте это:
httpResponse.getFirstHeader("Location").getValue();