1. Обзор
В этой статье мы покажем, как расширить URL-адреса с помощью HttpClient
.
Простой пример: исходный URL-адрес был сокращен один раз — такой службой, как bit.ly.
Более сложный пример: URL-адрес был сокращен несколько раз разными службами, и требуется несколько проходов, чтобы получить исходный полный URL-адрес.
Если вы хотите копнуть глубже и узнать о других интересных вещах, которые можно делать с помощью HttpClient, — перейдите к основному руководству по HttpClient .
2. Разверните URL один раз
Давайте начнем с простого, расширив URL-адрес, который был передан через службу сокращения URL-адресов только один раз.
Первое, что нам понадобится, это HTTP-клиент, который не следует автоматически за перенаправлениями :
CloseableHttpClient client =
HttpClientBuilder.create().disableRedirectHandling().build();
Это необходимо, потому что нам нужно будет вручную перехватывать ответ перенаправления и извлекать из него информацию.
Мы начнем с отправки запроса на сокращенный URL-адрес — ответ, который мы получим, будет 301 Moved Permanently
.
Затем нам нужно извлечь заголовок Location
, указывающий на следующий, и в данном случае — на конечный URL:
public String expandSingleLevel(String url) throws IOException {
HttpHead request = null;
try {
request = new HttpHead(url);
HttpResponse httpResponse = client.execute(request);
int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode != 301 && statusCode != 302) {
return url;
}
Header[] headers = httpResponse.getHeaders(HttpHeaders.LOCATION);
Preconditions.checkState(headers.length == 1);
String newUrl = headers[0].getValue();
return newUrl;
} catch (IllegalArgumentException uriEx) {
return url;
} finally {
if (request != null) {
request.releaseConnection();
}
}
}
Наконец, простой живой тест с «несокращенным» URL:
@Test
public final void givenShortenedOnce_whenUrlIsExpanded_thenCorrectResult() throws IOException {
final String expectedResult = "https://www.foreach.com/rest-versioning";
final String actualResult = expandSingleLevel("http://bit.ly/3LScTri");
assertThat(actualResult, equalTo(expectedResult));
}
3. Обработка нескольких уровней URL
Проблема с короткими URL-адресами заключается в том, что они могут многократно сокращаться совершенно разными службами. Для расширения такого URL-адреса потребуется несколько проходов, чтобы перейти к исходному URL-адресу.
Мы собираемся применить примитивную операцию expandSingleLevel,
определенную ранее, чтобы просто перебрать все промежуточные URL-адреса и добраться до конечной цели :
public String expand(String urlArg) throws IOException {
String originalUrl = urlArg;
String newUrl = expandSingleLevel(originalUrl);
while (!originalUrl.equals(newUrl)) {
originalUrl = newUrl;
newUrl = expandSingleLevel(originalUrl);
}
return newUrl;
}
Теперь, с новым механизмом расширения нескольких уровней URL-адресов, давайте определим тест и заставим его работать:
@Test
public final void givenShortenedMultiple_whenUrlIsExpanded_thenCorrectResult() throws IOException {
final String expectedResult = "https://www.foreach.com/rest-versioning";
final String actualResult = expand("http://t.co/e4rDDbnzmk");
assertThat(actualResult, equalTo(expectedResult));
}
На этот раз короткий URL-адрес — http://t.co/e4rDDbnzmk
— который на самом деле укорачивается дважды — один раз через bit.ly
и второй раз через сервис t.co
— правильно расширяется до исходного URL-адреса.
4. Обнаружение циклов перенаправления
Наконец, некоторые URL-адреса нельзя расширить, поскольку они образуют цикл перенаправления. HttpClient
обнаружит проблему такого типа , но, поскольку мы отключили автоматическое отслеживание перенаправлений, этого больше не происходит.
Последним шагом в механизме расширения URL будет обнаружение петель перенаправления и быстрый сбой в случае возникновения такой петли.
Чтобы это было эффективно, нам нужна дополнительная информация из метода expandSingleLevel
, который мы определили ранее — в основном нам нужно также вернуть код состояния ответа вместе с URL-адресом.
Поскольку java не поддерживает несколько возвращаемых значений, мы собираемся обернуть информацию в объект org.apache.commons.lang3.tuple.Pair
— новая сигнатура метода теперь будет:
public Pair<Integer, String> expandSingleLevelSafe(String url) throws IOException {
И, наконец, давайте включим обнаружение цикла редиректа в основной механизм расширения:
public String expandSafe(String urlArg) throws IOException {
String originalUrl = urlArg;
String newUrl = expandSingleLevelSafe(originalUrl).getRight();
List<String> alreadyVisited = Lists.newArrayList(originalUrl, newUrl);
while (!originalUrl.equals(newUrl)) {
originalUrl = newUrl;
Pair<Integer, String> statusAndUrl = expandSingleLevelSafe(originalUrl);
newUrl = statusAndUrl.getRight();
boolean isRedirect = statusAndUrl.getLeft() == 301 || statusAndUrl.getLeft() == 302;
if (isRedirect && alreadyVisited.contains(newUrl)) {
throw new IllegalStateException("Likely a redirect loop");
}
alreadyVisited.add(newUrl);
}
return newUrl;
}
Вот и все — механизм expandSafe
способен расширять URL-адреса, проходящие через произвольное количество служб сокращения URL-адресов, в то же время быстро терпя неудачу в циклах перенаправления.
5. Вывод
В этом руководстве обсуждалось, как расширить короткие URL-адреса в java — с помощью Apache HttpClient
.
Мы начали с простого варианта использования с URL-адресом, который был сокращен только один раз, а затем реализовали более общий механизм, способный обрабатывать несколько уровней перенаправления и обнаруживать циклы перенаправления в процессе.
Реализация этих примеров доступна на GitHub .