HttpClientを使った簡潔なURL
1概要
この記事では、
HttpClient
を使用してURLを
短縮する方法を説明します。
簡単な例は、
bit.ly
のようなサービスによって、元のURLが1回短縮された場合です。
より複雑な例は、そのようなサービスによって
URLが複数回短縮された
場合で、元の完全なURLに到達するまでに複数のパスが必要です。
もっと深く掘り下げて、HttpClientを使ってできることを他にも学びたい場合は、
HttpClientのメインチュートリアル
に進んでください。
2一度URLの短縮を解除
簡単に始めましょう。短縮URLサービスを1回だけ通過した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
)が実際には2回短縮されています(1回は
bit.ly
を介して、もう1回は
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結論
このチュートリアルでは、Apacheの
HttpClient
を使用して、Javaで短いURLを** 展開する方法について説明しました。
私たちは一度だけ短縮されたURLを使った簡単なユースケースから始めて、そしてより一般的なメカニズムを実装しました。
これらの例の実装はhttps://github.com/eugenp/tutorials/tree/master/httpclient[githubプロジェクト]にあります。これはEclipseベースのプロジェクトなので、インポートして実行するのは簡単なはずです。です。