web-dev-qa-db-ja.com

HTML:フォームはUTF-8形式の入力を送信しません

HTMLでのUTF-8エンコーディングに関する質問を1つ1つ訪問しましたが、期待どおりに機能しているようには見えません。

metaタグを追加しました。何も変更されていません。
私はaccept-charsetformの属性:何も変更されていません。


JSPファイル

<%@ page pageEncoding="UTF-8" %>
<%@ taglib uri="http://Java.Sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Editer les sous-titres</title>
</head>
<body>
    <form method="post" action="/Subtitlor/edit" accept-charset="UTF-8"> 
        <h3 name="nameOfFile"><c:out value="${ nameOfFile }"/></h3> 
        <input type="hidden" name="nameOfFile" id="nameOfFile" value="${ nameOfFile }"/>
        <c:if test="${ !saved }">
            <input value ="Enregistrer le travail" type="submit" style="position:fixed; top: 10px; right: 10px;" />
        </c:if>
        <a href="/Subtitlor/" style="position:fixed; top: 50px; right: 10px;">Retour à la page d'accueil</a>
        <c:if test="${ saved }">
            <div style="position:fixed; top: 90px; right: 10px;">
                <c:out value="Travail enregistré dans la base de donnée"/>
            </div>
        </c:if>
        <table border="1">
            <c:if test="${ !saved }">
                <thead>
                    <th style="weight:bold">Original Line</th>
                    <th style="weight:bold">Translation</th>
                    <th style="weight:bold">Already translated</th>
                </thead>
            </c:if>
            <c:forEach items="${ subtitles }" var="line" varStatus="status">
                <tr>
                    <td style="text-align:right;"><c:out value="${ line }" /></td>
                    <td><input type="text" name="line${ status.index }" id="line${ status.index }" size="35" /></td>
                    <td style="text-align:right"><c:out value="${ lines[status.index].content }"/></td>
                </tr>
            </c:forEach>
        </table>
    </form>
</body>
</html>

サーブレット

for (int i = 0 ; i < 2; i++){
    System.out.println(request.getParameter("line"+i));
}

出力

Et ton père et sa soeur
Il ne sera jamais parti.
11
Yassin Hajaj

metaタグを追加しました:何も変更されませんでした。

実際に、ページがHTTP経由ではなくHTTP経由で提供されている場合は効果がありません。ローカルディスクファイルシステムから(つまり、ページのURLは_http://..._ではなく_file://..._です)。 HTTPでは、HTTP応答ヘッダーの文字セットが使用されます。次のように設定済みです。

_<%@page pageEncoding="UTF-8"%>
_

これにより、UTF-8を使用してHTTP応答が書き出されるだけでなく、_Content-Type_応答ヘッダーにcharset属性も設定されます。

これは、Webブラウザーが応答を解釈し、HTMLフォームのパラメーターをエンコードするために使用されます。


formに_accept-charset_属性を追加しました:何も変更されませんでした。

Microsoft Internet Explorerブラウザーでのみ効果があります。それでもそれは間違ってそれをしています。絶対に使用しないでください。実際のすべてのWebブラウザーは、代わりに応答の_Content-Type_ヘッダーで指定されたcharset属性を使用します。 MSIEでも、_accept-charset_属性を指定するしない限り、正しく実行されます。前に述べたように、pageEncodingですでに適切に設定されています。


metaタグと_accept-charset_属性の両方を削除します。それらは有益な効果はなく、長期的に混乱するだけでなく、エンドユーザーがMSIEを使用する場合に事態を悪化させるだけです。 pageEncodingに固執するだけです。すべてのJSPページでpageEncodingを繰り返す代わりに、以下のように_web.xml_でグローバルに設定することもできます。

_<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <page-encoding>UTF-8</page-encoding>
    </jsp-property-group>
</jsp-config>
_

前述のように、これにより、JSPエンジンはUTF-8を使用してHTTP応答出力を書き込み、HTTP応答ヘッダーにも設定するようになります。 Webブラウザーは、サーバーに送り返す前に、同じ文字セットを使用してHTTP要求パラメーターをエンコードします。

不足している唯一の手順は、getParameterXxx()呼び出しで戻る前に、HTTP要求パラメーターをデコードするためにUTF-8を使用する必要があることをサーバーに伝えることです。これをグローバルに行う方法は、HTTP要求メソッドによって異なります。 POSTメソッドを使用している場合、これは、すべての要求を自動的にフックする以下のサーブレットフィルタークラスで比較的簡単に実現できます。

_@WebFilter("/*")
public class CharacterEncodingFilter implements Filter {

    @Override
    public void init(FilterConfig config) throws ServletException {
        // NOOP.
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // NOOP.
    }
}
_

それで全部です。サーブレット3.0以降(Tomcat 7以降)では、追加の_web.xml_構成は必要ありません。

setCharacterEncoding()メソッドがbeforebefore POSTリクエストが呼び出されることが非常に重要であることを覚えておく必要がありますパラメータは、getParameterXxx()メソッドのいずれかを使用して初めて取得されます。これは、最初のアクセスで一度だけ解析され、サーバーのメモリにキャッシュされるためです。

したがって、たとえば以下のシーケンスはwrongです:

_String foo = request.getParameter("foo"); // Wrong encoding.
// ...
request.setCharacterEncoding("UTF-8"); // Attempt to set it.
String bar = request.getParameter("bar"); // STILL wrong encoding!
_

サーブレットフィルタでsetCharacterEncoding()ジョブを実行すると、(少なくともサーブレットの前に)タイムリーに実行されることが保証されます。


サーバーにUTF-8を使用してGET(POSTではなく)要求パラメーター(URLの_?_文字の後に表示されるパラメーター)もデコードするように指示する場合は、基本的に次のようにする必要があります。サーバー側で構成します。サーブレットAPIを介して構成することはできません。たとえば、Tomcatをサーバーとして使用している場合は、Tomcat独自の_URIEncoding="UTF-8"_の_<Connector>_要素に_/conf/server.xml_属性を追加するだけです。

System.out.println()呼び出しのコンソール出力に Mojibake が引き続き表示される場合は、stdout自体がUTF-8を使用するように構成されていない可能性が高くなります。それを行う方法は、stdoutを解釈して提示する責任がある人によって異なります。たとえばEclipseをIDEとして使用している場合は、Window> Preferences> General> Workspace> Text File EncodingをUTF-8に設定するだけです。

以下も参照してください。

31
BalusC

準備し始める

まず、コンピュータはビット(0と1)以外は何も理解しないことを知っているという普遍的な事実から始めましょう。

ここで、HTTPを介してHTMLフォームを送信し、値が回線を経由して宛先サーバーに到達すると、基本的には多くのビット-0と1が渡されます。

  • サーバーにデータを送信する前に、HTTPクライアント(ブラウザーまたはcurlなど)は、何らかのエンコードスキームを使用してデータをエンコードし、サーバーが同じスキームを使用してデータをデコードすることを期待して、サーバーがクライアントが送信した内容を正確に認識できるようにします。
  • クライアントに応答を送信する前に、サーバーは何らかのエンコードスキームを使用してそれをエンコードし、クライアントが同じスキームを使用してデコードすることを期待して、クライアントがサーバーが送信した内容を正確に認識できるようにします。

これのアナロジーは次のようになります-私はあなたに手紙を送り、それが英語、フランス語、またはオランダ語のどちらで書かれているかを伝えます。私があなたに送るつもりであったように正確なメッセージを受け取ります。そして、私に返信する際に、私がどの言語で読むべきかについても言及します。

重要なポイントは、データがクライアントから送信されるときにエンコードされ、サーバー側でも同じようにデコードされるという事実です。何も指定しない場合、コンテンツは application/x-www-form-urlencoded に従ってエンコードされてから、クライアント側からサーバー側に移動します。

核心概念

ウォームアップを読むことは重要です。期待される結果を得るために確認する必要があることがいくつかあります。

  • クライアントからサーバーにデータを送信する前に、正しいエンコーディングが設定されている。
  • サーバー側で正しいデコードとエンコードを設定して、要求を読み取り、クライアントに応答を書き込みます(これが、予期した結果が得られなかった理由です
  • すべての場所で同じエンコードスキームが使用されていることを確認してください。クライアントでISO-8859-1を使用してエンコードし、サーバーでUTF-8を使用してデコードしている場合は、発生しないはずです。そうしないと、誤動作(私のアナロジーから、私はあなたを英語で書いていて、あなたはフランス語で読んでいます
  • WindowsコマンドラインまたはEclipseログビューアなどを使用してログを確認しようとした場合、ログビューアに正しいエンコーディングが設定されている(これは問題の原因でしたが、リクエストオブジェクトから読み取ったデータが正しくデコードされなかった最初の場所Windows cmdまたはEclipseログビューアのエンコーディングも重要です、readhere

クライアントからサーバーにデータを送信する前に正しいエンコーディングを設定する

これを確実にするために、いくつかの方法が話されていますが、私は HTTP Accept-Charset request-header field を使用すると言います。提供されたコードスニペットに従って、すでに使用しており、正しく使用しているため、その面で優れています。

これを使用しない、または実装されていないと言う人もいますが、私は非常に謙虚に反対します。 _Accept-Charset_はHTTP 1.1仕様の一部であり(私はリンクを提供しました)、HTTP 1.1を実装するブラウザは同じものを実装します。 Accept request-header field's "charset"属性を使用すると主張するかもしれませんが、

  • 本当に存在しない場合は、提供したAccept request-headerフィールドリンクを確認してください。
  • チェック これ

言葉だけでなく、すべてのデータと事実を提供しますが、それでも満足できない場合は、さまざまなブラウザを使用して次のテストを行ってください。

  • サーバーに中国語または高度なフランス語の文字を含むHTMLフォームおよびPOST/GETフォームで_accept-charset="ISO-8859-1"_を設定します。
  • サーバーで、UTF-8スキームを使用してデータをデコードします。
  • クライアントとサーバーのエンコーディングを入れ替えて、同じテストを繰り返します。

サーバーで予期された文字を見ることができなかったことがわかります。ただし、同じコード化スキームを使用する場合は、期待される文字が表示されます。したがって、ブラウザは_accept-charset_を実装し、その効果が発揮されます。

サーバー側で正しいデコードとエンコードを設定して、要求を読み取り、クライアントに応答を書き込む

これを達成するためにあなたができることについて話している地獄の多くの方法があります(特定のシナリオに基づいていくつかの構成が必要になる場合がありますが、以下は95%のケースを解決し、あなたのケースにも適しています)。例えば:

  1. リクエストとレスポンスのエンコーディングを設定するには、文字エンコーディングフィルタを使用します。
  2. リクエストとレスポンスにsetCharacterEncodingを使用
  3. _-Dfile.encoding=utf8_などを使用して、Webまたはアプリケーションサーバーを正しい文字エンコーディングに設定します。続きを読む こちら
  4. 等。

私のお気に入りは最初のもので、あなたの問題も解決します-以下の理由により「文字エンコーディングフィルター」:

  • エンコーディング処理ロジックはすべて1か所にあります。
  • あなたは設定を通してすべての力を持っています、一か所で、そして幸せなら誰でも変更してください。
  • 文字エンコーディングを設定する前に、他のコードがリクエストストリームを読み取ったり、レスポンスストリームをフラッシュしたりすることを心配する必要はありません。

1.文字エンコードフィルター

以下を実行して、独自の文字エンコーディングフィルターを実装できます。 Springsなどのフレームワークを使用している場合は、独自のクラスを記述する必要はなく、web.xmlで構成するだけです

以下のコアロジックは、Springが行うことと非常によく似ていますが、多くの依存関係は別として、Beanが行うことを認識しています。

web.xml(構成)

_<filter>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>
        com.sks.hagrawal.EncodingFilter
    </filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
_

EncodingFilter(文字エンコーディング実装クラス)

_public class EncodingFilter implements Filter {
    private String encoding = "UTF-8";
    private boolean forceEncoding = false;

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        request.setCharacterEncoding(encoding);
        if(forceEncoding){ //If force encoding is set then it means that set response stream encoding as well ...
            response.setCharacterEncoding(encoding);
        }
        filterChain.doFilter(request, response);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        String encodingParam = filterConfig.getInitParameter("encoding");
        String forceEncoding = filterConfig.getInitParameter("forceEncoding");
        if (encodingParam != null) {
            encoding = encodingParam;
        }
        if (forceEncoding != null) {
            this.forceEncoding = Boolean.valueOf(forceEncoding);
        }
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }
}
_

2. ServletRequest.setCharacterEncoding()

これは基本的に文字エンコーディングフィルターで行われるのと同じコードですが、フィルターで行うのではなく、サーブレットまたはコントローラークラスで行います。

ここでも、httpリクエストストリームの読み取りを開始する前に、request.setCharacterEncoding("UTF-8");を使用してhttpリクエストストリームのエンコーディングを設定することをお勧めします。

以下のコードを試してみてください。リクエストオブジェクトのエンコーディングを設定するためになんらかのフィルタを使用していない場合、最初のログはNULLになり、2番目のログは「UTF-8」になります。

_System.out.println("CharacterEncoding = " + request.getCharacterEncoding());
request.setCharacterEncoding("UTF-8");
System.out.println("CharacterEncoding = " + request.getCharacterEncoding());
_

以下は setCharacterEncoding Java docs からの重要な抜粋です。注意すべきもう1つの点は、有効なエンコードスキームを提供する必要があることです。それ以外の場合はUnsupportedEncodingException

オーバーライドこのリクエストの本文で使用される文字エンコーディングの名前。 このメソッドは、リクエストパラメータを読み取る前、またはgetReader()を使用して入力を読み取る前に呼び出す必要があります。それ以外の場合、影響はありません。

あなたが信頼を築くことができるように、必要なところはどこでも、私はあなたに公式リンクまたはStackOverflowが受け入れた賞金の回答を提供するために最善を尽くしました。

6
hagrawal

あなたの投稿された出力に基づいて、パラメータはUTF8として送信され、後で文字列のUnicodeバイトがISO-8859-1として解釈されるようです。

次のスニペットは、観察された動作を示しています

String eGrave = "\u00E8"; // the letter è
System.out.printf("letter UTF8      : %s%n", eGrave);
byte[] bytes = eGrave.getBytes(StandardCharsets.UTF_8);
System.out.printf("UTF-8 hex        : %X %X%n",
        bytes[0], bytes[1], bytes[0], bytes[1]
);
System.out.printf("letter ISO-8859-1: %s%n",
        new String(bytes, StandardCharsets.ISO_8859_1)
);

出力

letter UTF8      : è
UTF-8 hex        : C3 A8
letter ISO-8859-1: è

私にとってフォームは正しいUTF8エンコードされたデータを送信しますが、後でこのデータはUTF8として扱われません。

編集試してみるべきその他のポイント:

リクエストの文字エンコーディングを出力する

System.out.println(request.getCharacterEncoding())

パラメータを取得するためにUTF-8の使用を強制します(テストされていない、アイデアのみ)

request.setCharacterEncoding("UTF-8");
... request.getParameter(...);
2
SubOptimal

あなたはそれを.jspに書くことを試みることができます:

<%@ page language="Java" contentType="text/html; charset=ISO-8859-1"
         pageEncoding="UTF-8"%>

それで問題は解決しました。

1
nicowtt

JSPコードのcharsetおよびpageEncoding定義でISOに関連する文字列を使用できます。

Charset = "ISO-8859-1"やpageEncoding = "ISO-8859-1"と同様です。

0
IndTechVJ

Tomcatには、あなたを引っ掛かるバグがあります。最初のフィルターは、要求が基づくエンコーディングを定義します。

最初のフィルターの背後にある他のすべてのフィルターまたはサーブレットは、要求のエンコーディングを変更できなくなります。

現在のアプリケーションはエンコーディングに依存している可能性があるため、このバグは将来修正されるとは思いません。

0
Peter Rader