1

一部の Web サイトのコンテンツの取得に問題があります。国際文字を含む別の URL にリダイレクトする URL に移動しようとすると、Java は通常エラー 404 を取得します。ブラウザでこの URL をたどると、有効なデータが得られます。

たとえば、hXXp://shar.es/cISmv に移動したい (2 つ以上の有効なリンクを投稿することはできません)

ブラウザは hXXp://www.dandy-magazine.com/la-griffe-de-la-tour-d%E2%80%99argent に正しくリダイレ​​クトします。wget から、最初にサイトが既存の「場所: http://www.dandy-magazine.com/la-griffe-de-la-tour-d%E2%80%99argent」でリダイレクト 301 を返すことがわかります。

Java (リダイレクトがオフになっている場合) では、リダイレクト 301 に " Location: http://www.dandy-magazine.com/la-griffe-de-la-tour-dâargent" が返されます。URL エンコーディングを使用すると、次のようになります: " http://www.dandy-magazine.com/la-griffe-de-la-tour-d%C3%A2%C2%80%C2%99argent". ご覧のとおり、まったく別のサイトです。

サンプルコード (基本的にバージョン 1 とバージョン 2 は同じことを行います):

// version 1 - let java handle redirects
URL url = new URL("http://shar.es/cISmv");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setInstanceFollowRedirects(true);
con.getResponseCode();
return con.getURL(); // returned url is not what it should be

// version 2 - I want to handle redirects
URL url = new URL("http://shar.es/cISmv");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setInstanceFollowRedirects(false);
con.getResponseCode();
String loc = con.getHeaderField("Location");
// here is the problem, loc is not initialized with a correct url
// returned String corresponds to url returned in version 1

助けてくれてありがとう

4

1 に答える 1

1

私が知る限り、値が UTF-8 でエンコードされている場合、Java は Location ヘッダーを処理していません。

URL は .../la-griffe-de-la-tour-d'argent のようになります。前の文では、ASCII 単一引用符を使用していることに注意してください。ただし、単一引用符文字を使用する代わりに、Web サイトは Unicode 文字を使用することを選択しました。

00002019 RIGHT SINGLE QUOTATION MARK
Glyph: ’
UTF-8: E2 80 99

Wireshark トレースは、返された Location ヘッダーにこの文字が UTF-8 でエンコードされていることを示しています。

00e0  65 70 2d 61 6c 69 76 65  0d 0a 4c 6f 63 61 74 69   ep-alive ..Locati
00f0  6f 6e 3a 20 68 74 74 70  3a 2f 2f 77 77 77 2e 64   on: http ://www.d
0100  61 6e 64 79 2d 6d 61 67  61 7a 69 6e 65 2e 63 6f   andy-mag azine.co
0110  6d 2f 6c 61 2d 67 72 69  66 66 65 2d 64 65 2d 6c   m/la-gri ffe-de-l
0120  61 2d 74 6f 75 72 2d 64  e2 80 99 61 72 67 65 6e   a-tour-d ...argen
0130  74 0d 0a 0d 0a 30 0d 0a  0d 0a                     t....0.. ..      

これが合法的な HTTP かどうかはわかりません。これについては、ウェブ上で間違いなく多くの質問があります。合法かどうかに関係なく、HttpURLConnection クラスはそれをうまく処理できません。への呼びかけ

String loc = con.getHeaderField("Location");

と の間にhttp://www.dandy-magazine.com/la-griffe-de-la-tour-d’argent単一の文字 (2019) がある文字列を返す必要がありました。代わりに、これらの 3 つの UTF-8 バイトのそれぞれを文字 (E2 80 99) に変換することによって、無効な文字列を返します。この時点で、「loc」文字列は役に立ちません。有効な Unicode 文字列ではありません。dargent

役立つ可能性のある回避策を次に示します。

  String loc = con.getHeaderField("Location");
  byte [] locbytes = new byte[loc.length()];
  for (int index = 0; index < locbytes.length; index++)
  {
     locbytes[index] = (byte) loc.charAt(index);
  }

  // use this loc instead
  String loc2 = new String(locbytes, "UTF-8");

偽の文字列 (各文字には、Web サーバーから送信されたバイトの値が含まれます) をバイト配列に変換します。次に、適切な文字セットを使用してバイト配列を文字列に変換します。loc2 を URL として使用して、新しい接続を開きます。

おそらくこれを行うためのより良い方法がありますが、ヘッダー値を UTF-8 エンコードとして扱うように HttpURLConnection クラスに指示する方法があることを理解するためにソース実装を調べていません。

于 2012-11-12T18:21:09.170 に答える