HttpClient 4 – как захватить последний URL-адрес перенаправления
-
12-09-2019 - |
Вопрос
У меня довольно простой код 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()
возвращает исходный адрес вызова
Решение
Это будет текущий 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);
}
ИМХО, улучшенный способ, основанный на решении 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);
Я нашел это на Документация клиента 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();
}
Я думаю, что более простой способ найти последний 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();