«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 void givenShortenedOnce_whenUrlIsUnshortened_thenCorrectResult() throws IOException {
String expectedResult = "/rest-versioning";
String actualResult = expandSingleLevel("http://bit.ly/13jEoS1");
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 void givenShortenedMultiple_whenUrlIsUnshortened_thenCorrectResult() throws IOException {
String expectedResult = "/rest-versioning";
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 — это проект на основе Eclipse, поэтому его легко импортировать и запускать как есть.