使用Apache HTTPClient扩展URL
1.概述
在本文中,我们将展示如何使用HttpClient扩展 URL 。
一个简单的例子是原始 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 = "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 = "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,同时在重定向循环中快速正确地失败。