web-dev-qa-db-ja.com

サーブレットに渡されたUnicodeパラメータを正しくデコードするにはどうすればよいですか

私が持っていると仮定します:

_<a href="http://www.yahoo.com/" target="_yahoo" 
    title="Yahoo!&#8482;" onclick="return gateway(this);">Yahoo!</a>
<script type="text/javascript">
function gateway(lnk) {
    window.open(SERVLET +
        '?external_link=' + encodeURIComponent(lnk.href) +
        '&external_target=' + encodeURIComponent(lnk.target) +
        '&external_title=' + encodeURIComponent(lnk.title));
    return false;
}
</script>
_

_external_title_が_Yahoo!%E2%84%A2_としてエンコードされ、SERVLETに渡されることを確認しました。 SERVLETにある場合:

_Writer writer = response.getWriter();
writer.write(request.getParameter("external_title"));
_

ブラウザにYahoo!⢢が表示されます。ブラウザーの文字エンコードを手動でUTF-8に切り替えると、Yahoo!TM(これは私が望むものです)。

そのため、ブラウザーに送信していたエンコードが間違っていると考えました(_Content-type: text/html; charset=ISO-8859-1_でした)。 SERVLETを次のように変更しました:

_response.setContentType("text/html; charset=utf-8");
Writer writer = response.getWriter();
writer.write(request.getParameter("external_title"));
_

現在、ブラウザの文字エンコーディングはUTF-8ですが、出力はYahoo!â¢であり、ブラウザに正しい文字を表示させることができません。

私の質問は:_Content-type_および/またはnew String(request.getParameter("external_title").getBytes(), "UTF-8");および/またはYahoo!TMSERVLET出力に表示されますか?

36
Grant Wagner

あなたはもうすぐそこにいます。 EncodeURIComponentはUTF-8に正しくエンコードします。これは、今日のURLで常に使用する必要があるものです。

問題は、getParameter()がUTF-8の代わりにISO-8559-1を使用するため、送信されたクエリ文字列がサーバー側スクリプトへの途中で切断されることです。これは、WebがURI/IRIのUTF-8に落ち着く前のAncient Timesに由来しますが、実際に一致するようにサーブレット仕様が更新されていないか、少なくとも信頼できるサポートされたオプションを提供していないことはむしろ哀れです。

(サーブレット2.3にはrequest.setCharacterEncodingがありますが、クエリ文字列の解析には影響しません。また、他のフレームワーク要素によって単一のパラメーターが以前に読み込まれた場合、まったく機能しません。)

そのため、適切なUTF-8を取得するには、コンテナー固有のメソッドをいじる必要があります。多くの場合、server.xmlに含まれるものが含まれます。これは、どこでも動作するはずのWebアプリを配布するのにまったく役に立たない。 Tomcatについては http://wiki.Apache.org/Tomcat/FAQ/CharacterEncoding および Tomcatの「URIEncoding」、エンコーディングフィルター、request.setCharacterEncodingの違いは何ですか

45
bobince

同じ問題が発生し、URLDecoder()を使用してRequest.getQueryString()をデコードし、パラメーターを抽出して解決しました。

String[] Parameters = URLDecoder.decode(Request.getQueryString(), 'UTF-8')
                       .splitat('&');
19
Modi

Java(server.xmlをいじる必要はありません)でそれを行う方法があります。

動作しない :

protected static final String CHARSET_FOR_URL_ENCODING = "UTF-8";

String uname = request.getParameter("name");
System.out.println(uname);
// ÏηγÏÏÏÏη
uname = request.getQueryString();
System.out.println(uname);
// name=%CF%84%CE%B7%CE%B3%CF%81%CF%84%CF%83%CF%82%CE%B7
uname = URLDecoder.decode(request.getParameter("name"),
        CHARSET_FOR_URL_ENCODING);
System.out.println(uname);
// ÏηγÏÏÏÏη // !!!!!!!!!!!!!!!!!!!!!!!!!!!
uname = URLDecoder.decode(
        "name=%CF%84%CE%B7%CE%B3%CF%81%CF%84%CF%83%CF%82%CE%B7",
        CHARSET_FOR_URL_ENCODING);
System.out.println("query string decoded : " + uname);
// query string decoded : name=τηγρτσςη
uname = URLDecoder.decode(new String(request.getParameter("name")
        .getBytes()), CHARSET_FOR_URL_ENCODING);
System.out.println(uname);
// ÏηγÏÏÏÏη // !!!!!!!!!!!!!!!!!!!!!!!!!!!

作品 :

final String name = URLDecoder
        .decode(new String(request.getParameter("name").getBytes(
                "iso-8859-1")), CHARSET_FOR_URL_ENCODING);
System.out.println(name);
// τηγρτσςη

動作しましたが、 デフォルトのエンコーディング!= utf-8の場合は壊れます -代わりにこれを試してください(decode()の呼び出しは不要です):

final String name = new String(request.getParameter("name").getBytes("iso-8859-1"),
        CHARSET_FOR_URL_ENCODING);

上記のようにserver.xmlが次のように乱れている場合:

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1"
                     redirectPort="8443"  URIEncoding="UTF-8"/> 

URIEncoding="UTF-8"に注意してください)上記のコードは壊れます(getBytes("iso-8859-1")getBytes("UTF-8")を読み取る必要があるため) 。したがって、防弾ソリューションの場合は、URIEncoding属性の値を取得する必要があります。残念ながら、これはコンテナ固有のようです-さらに悪いことにコンテナのバージョン固有です。 Tomcat 7の場合、次のようなものが必要です。

import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;

import org.Apache.catalina.Server;
import org.Apache.catalina.Service;
import org.Apache.catalina.connector.Connector;

public class Controller extends HttpServlet {

    // ...
    static String CHARSET_FOR_URI_ENCODING; // the `URIEncoding` attribute
    static {
        MBeanServer mBeanServer = MBeanServerFactory.findMBeanServer(null).get(
            0);
        ObjectName name = null;
        try {
            name = new ObjectName("Catalina", "type", "Server");
        } catch (MalformedObjectNameException e1) {
            e1.printStackTrace();
        }
        Server server = null;
        try {
            server = (Server) mBeanServer.getAttribute(name, "managedResource");
        } catch (AttributeNotFoundException | InstanceNotFoundException
                | MBeanException | ReflectionException e) {
            e.printStackTrace();
        }
        Service[] services = server.findServices();
        for (Service service : services) {
            for (Connector connector : service.findConnectors()) {
                System.out.println(connector);
                String uriEncoding = connector.getURIEncoding();
                System.out.println("URIEncoding : " + uriEncoding);
                boolean use = connector.getUseBodyEncodingForURI();
                // TODO : if(use && connector.get uri enc...)
                CHARSET_FOR_URI_ENCODING = uriEncoding;
                // ProtocolHandler protocolHandler = connector
                // .getProtocolHandler();
                // if (protocolHandler instanceof Http11Protocol
                // || protocolHandler instanceof Http11AprProtocol
                // || protocolHandler instanceof Http11NioProtocol) {
                // int serverPort = connector.getPort();
                // System.out.println("HTTP Port: " + connector.getPort());
                // }
            }
        }
    }
}

それでも、複数のコネクタに対してこれを微調整する必要があります(コメント化された部分を確認してください)。次に、次のようなものを使用します。

new String(parameter.getBytes(CHARSET_FOR_URI_ENCODING), CHARSET_FOR_URL_ENCODING);

それでもこれは失敗する可能性があります( [〜#〜] iiuc [〜#〜] )CHARSET_FOR_URI_ENCODINGでデコードされたparameter = request.getParameter("name");が破損しているため、getBytes()で取得したバイトが元のバイトではなかった場合(だからこそ、デフォルトで "iso-8859-1"が使用されます- バイトを保存します )。次の行のクエリ文字列を手動で解析することにより、これらをすべて取り除くことができます。

URLDecoder.decode(request.getQueryString().split("=")[1],
        CHARSET_FOR_URL_ENCODING);

私はまだrequest.getParameter("name")%CF%84%CE%B7%CE%B3%CF%81%CF%84%CF%83%CF%82%CE%B7文字列を返す代わりにURLDecoder.decode()を呼び出すと言及されているドキュメント内の場所を探していますか?ソース内のリンクをいただければ幸いです。
また、%CEなどの文字列をパラメーターの値として渡すにはどうすればよいですか? =>コメントを参照:parameter=%25CE

16
Mr_and_Mrs_D

リクエストでデータの破損が発生していると思われます。つまり、リクエストの宣言されたエンコードが、実際にデータに使用されているエンコードと一致していません。

request.getCharacterEncoding()は何を返しますか?

JavaScriptがエンコードを処理する方法や、特定のエンコードを使用する方法を実際には知りません。

エンコードがすべての段階で正しく使用されていることを確認する必要があります-既に正しくエンコードされていない場所でnew String()getBytes()を使用してデータを「修正」しようとしないでください。 。

編集: Originページ(Javascriptのあるページ)をUTF-8でエンコードし、Content-Typeでそのように宣言すると役立つ場合があります。それから、JavascriptはデフォルトでリクエストにUTF-8を使用するかもしれませんが、これは明確な知識ではなく、単なる当て推量です。

2

私は次のものを機能させることができると思います:

_encodeURIComponent(escape(lnk.title))
_

これにより、_%25u2122_(&#8482の場合)または_%25AE_(&#174の場合)が得られ、サーブレットでそれぞれ_%u2122_および_%AE_にデコードされます。

その後、一致で_'\u2122'_を使用して比較的簡単に%u2122を_'\u00AE'_に、%AEを_(char) (base-10 integer value of %uXXXX or %XX)_に変換し、正規表現を使用してループを置き換えることができるはずです。

すなわち-/%u([0-9a-f]{4})/iに一致し、一致する部分式を抽出し、それを10進数に変換し、charに変換して出力に追加し、/%([0-9a-f]{2})/iで同じことを行います

0
Grant Wagner

Tomcat、jettyで使用するデフォルトの文字セットのエンコードデコードについて知ってくれたすべてのおかげで、Google guavaを使用してこの方法を使用して問題を解決します

        String str = URLDecoder.decode(request.getQueryString(), StandardCharsets.UTF_8.name());
        final Map<String, String> map = Splitter.on('&').trimResults().withKeyValueSeparator("=").split(str);
        System.out.println(map);
        System.out.println(map.get("aung"));
        System.out.println(map.get("aa"));
0
Aung Aung

Jettyの特定のバージョンには、より大きな数値のUTF-8文字を誤って解析するバグがあります。サーバーがアラビア文字を正しく受け入れているが絵文字を受け入れていない場合、アラビア語はISO-8859-1ではなく、UTF-8文字の下位範囲(「下位」の意味= Javaは単一の文字で表現します)。

バージョン7.2.0.v20101020からバージョン7.5.4.v20111024に更新すると、問題が修正されました。自分で解析する代わりに、getParameter(String)メソッドを使用できるようになりました。

好奇心が強い場合は、org.Eclipse.jetty.util.Utf8StringBuilder.append(byte)のバージョンを調べて、utf-8コードが十分に高い場合、または文字列に複数の文字を正しく追加できるかどうかを確認できます。 、7.2.0のように、単にintをcharにキャストして追加します。

0
Ben B

テキストをさらに操作するには、常にJavaScriptを使用できます。

<div id="test">a</div>
<script>
var a = document.getElementById('test');
alert(a.innerHTML);
a.innerHTML = decodeURI("Yahoo!%E2%84%A2");
alert(a.innerHTML);
</script>
0
jacobangel