(0)目次&概説
(1) 事象
(2) 原因
(3) 対処方法1
(3-1) フィルタクラスの新規作成
(3-2) フィルタクラスへのコード追加
(3-3) 疎通確認テスト
(4) 対処方法2
(4-1) setContentTypeの場所修正
(5) 解説
(5-1) フィルタクラスとは?
(5-2) フィルタはどのタイミングで発動する?
(5-3) フィルタクラスのプログラム概要
(1) 事象
サーブレットの画面にて日本語の文字を表示しようとすると「???」と文字化けしてしまった。
(図101)
(2) 原因
HttpServretRequestのgetWriterを呼び出す前に「response.setContentType(“text/html; charset=UTF-8”);」で文字コードの指定を行わなかったため、デフォルトの文字コード(ISO-8859-1)になってしまった事が原因です。そのため途中で「charset=UTF-8″」しても、上書きされずデフォルトの文字コードままappendされて文字化けしていました。
(図201)
本来は35行目の「getWriter」の前でcharsetの指定を行う必要がある。
(3) 対処方法1
対処方法の一つが「フィルタクラス」を追加して、出力時のエンコード処理をする事です。
(3-1) フィルタクラスの新規作成
(3-1-1) Eclipseのメニューから「File」→「New」→「Other」を選択します。
(図311)
(3-1-2) 「New」画面のWizards検索窓に「filter」と入力してフィルタークラスを探し選択します。
(図312)
(3-1-3) Class Name等の情報を設定して「Next」を押下します。
(図313)
(3-1-4) Filter mappingsの条件を設定します
「Filter mappings」でフィルタの適用範囲を指定します。デフォルトのレコードを選択して「Edit」で編集します。適用範囲は次のようにプロジェクト全体を指定したり、部分的に適用したり小回りが利きます。
(図314)
(表)
全体適用 (プロジェクト全体に適用) |
(1)ワイルドカード指定でプロジェクト全体に適用されます /* |
部分適用 (プロジェクトの特定箇所に適用) |
(1)JSPの場合 構文: /[JSP名].jsp 例: /Test1.jsp (2)サーブレットの場合 |
(3-1-5) 実装するフィルタの指定を行います
今回はデフォルトの「javax.servlet.Filter」インターフェイスを実装する設定で進みます。
(図316)
(3-2) フィルタクラスへのコード追加
フィルタクラスのdoFilterメソッド内の「chain.doFilter」の前にリクエストデータ(サーバから見た受信)の文字コードと、レスポンス(サーバから見た送信)の文字コードを指定します。
request.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8");
(図321)
次にTomcatを再起動します。
(図322)
(3-3) 疎通確認テスト
画面をもう一度開き直すと文字化けが解消している事が確認できます。
(図331)
(4) 対処方法2
(4-1) setContentTypeの場所修正
フィルタクラスの追加でも勿論対応できますが、よりピンポイントの対処方法としては、単にset
■修正前
response.getWriter().append("Served at: ").append(request.getContextPath());
↓ ■修正後
response.setContentType("text/html;charset=UTF-8"); response.getWriter().append("Served at: ").append(request.getContextPath());
(5) 解説
(5-1) フィルタクラスとは?
フィルタはServletやJSPが呼び出された時に一緒に呼び出され、フィルタリング等の付随処理を担います。付随処理の例としては、暗号化や認証やログの集計など様々あります。
フィルタクラスの強みは、ServletやJSPの中で明示的に呼び出し必要が無い点です。なので共通ロジックのフィルタを呼び出す行をServletやJSPに書いたりする必要はなく、メンテナンスの負荷が低く済みます。
(5-2) フィルタはどのタイミングで発動する?
デフォルトでは本来のJSPやサーブレットのURLが呼び出された時に起動しますが(REQUEST)、web.xmlの<filter-mapping>要素等でタイミングを任意に指定する事もできます。
流れとしては次の図のようになります。「①クライアント」からのリクエストが「②トリガー」により検知されWebAPサーバに届き、「③サーブレットコンテナ」にて、今回のテーマである「フィルタ処理」がされ、実行エンジンにより要求ページの解析・処理を行います。
(図521)
(5-3) フィルタクラスのプログラム概要
今回使用したフィルタクラスのサンプルプログラムを紹介します。
package filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; /** * Servlet Filter implementation class TestFilter */ @WebFilter("/*") public class TestFilter implements Filter { /** * Default constructor. */ public TestFilter() { } public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); // pass the request along the filter chain chain.doFilter(request, response); } public void init(FilterConfig fConfig) throws ServletException { } }
■プログラム解説
行数 | 処理概要 |
16行目 | “implements Filter”から分かる通り、Filterインターフェイスを実装しています。フィルタに必要なメソッドがFilterインターフェイスに定義されており、これらを実装する事でフィルタを自在に作れます。 注意事項として、インターフェイスを実装する際は例えそのメソッドが不要であっても、空のメソッドとして雛形の記述を行わないとエラーになります(図531)。 例えば、今回の例ではinitメソッドやdestroyメソッドは実装していませんが、定義の記述自体がないとエラーになるので空のメソッドを記述しています。 |
27行目 | (1) メソッドの引数が「ServletRequest」と「ServletResponse」オブジェクトのため、もし「HttpServletRequest」や「HttpServletResponse」型のオブジェクトの機能が必要な場合は型のキャスト等が必要となります。
(2) doFilterメソッドは登録されたフィルタの集まりの中から、次のフィルタを起動します。起動対象が無い場合は本来の要求ページを表示します。 |
(図531)
インターフェイスに存在するメソッドは空でも記述しないとエラーになる