Перейти к основному содержимому

Расширение сокращенных URL-адресов с помощью Apache HttpClient

· 4 мин. чтения

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 .