HttpClient 4 - 마지막 리디렉션 URL을 캡처하는 방법
-
12-09-2019 - |
문제
HTML 출력을 얻기 위해 HttpGet을 호출하는 간단한 HttpClient 4 코드가 있습니다.HTML은 모두 로컬로 설정된 스크립트 및 이미지 위치와 함께 반환됩니다(예: <img src="/images/foo.jpg"/>
) 따라서 이를 절대값으로 만들려면 URL을 호출해야 합니다(<img src="http://foo.com/images/foo.jpg"/>
) 이제 문제가 발생합니다. 통화 중에 1~2개의 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의 솔루션을 기반으로 하는 IMHO 개선 방법은 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;
}
}
여기에는 순환 리디렉션 보호가 없으므로 주의하세요.자세한 내용은 블로그를 통해 확인하세요 AndroidHttpClient를 사용하여 302 리디렉션을 따릅니다.
이것이 내가 리디렉션 URL을 얻는 방법입니다.
Header[] arr = httpResponse.getHeaders("Location");
for (Header head : arr){
String whatever = arr.getValue();
}
또는 리디렉션 위치가 하나만 있다고 확신하는 경우 다음을 수행합니다.
httpResponse.getFirstHeader("Location").getValue();