Javaサーブレットは文字コードのことを全く考えません。
考えないだけならまだしも、何もしなければ勝手にヘッダにISO-8859-1という文字コードを指定してしまいます。
ISO-8859-1は日本語を扱えないので、日本語を表示するためには、あらゆるサーブレットで毎回必ず
req.setCharacterEncoding("Windows-31J");
res.setContentType("text/html; charset=EUC-JP");
とヘッダを上書きするコードを書かなければなりません。
また、ショッピングサイト等、必ず上や左にメニューが付いてくるページがありますが、それを実現するためには全てのページでメニューを書かなければなりません。
Java以外の言語を使用しているサイトでは、メニュー部分を別に作成しておき毎回インクルードする、入口を一本化してパラメータで内容を振り分ける等の仕組みで実現しています
Javaでも同様にincludeする仕組みがいくつもあるのですが、それ以外にもJavaアプリケーションには最初からJavaサーブレットに横断的にフィルタを実装する仕組みがあるので使ってみましょう。
とりあえずweb.xmlから。
<filter> <filter-name>charsetFilter</filter-name> <filter-class>charsetFilter</filter-class> </filter> <filter-mapping> <filter-name>charsetFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
servletとほぼ同じ指定方法です。
servletはurl_patternにマッチしたJavaサーブレットを実行したあと終了しますが、filterは該当するJavaサーブレットを実行した後も、終了せずにservletをそのまま続けて実行します。
またservletは複数のURLにマッチした場合優先順位の高いほう一つしか実行されませんが、filterはマッチするサーブレットすべてが実行されます。
横断的に処理を行うのにうってつけのクラスです。
url-patternには前回までのような直接指定のほかにワイルドカード*が使えます。
Linuxの*ではなくMS-DOSの*なので注意が必要です。
<url-pattern>/*</url-pattern>と指定することで、すべてのクラスにフィルタを適用することができます。
ではfilter-classで指定したcharsetFilterを作成しましょう。
charsetFilter.java
import java.io.*; public class charsetFilter implements Filter { |
フィルタを行うクラスはFilterインターフェイスをimplementsしなければなりません。
Filterインターフェイスにはinit、destroy、doFilterの3つのメソッドが定義されていますが、initとdestroyは使わないので放置します。
<filter>が呼び出されるたびにdoFilterメソッドが呼び出されるのでこれを上書きすることでフィルタを実装します。
このdoFilterメソッドでは文字コードを指定しています。
全てのサーブレットは自動的にこのcharsetFilterクラスを通って実行されるので、今後すべてのサーブレットでsetContentTypeを書く必要がなくなります。
たとえば認証処理を行いたい場合、Javaサーブレット以外の言語では、すべてのファイルで認証用のルーチンを呼び出したりということを行わなければなりません。
Javaサーブレットでは、認証処理を行いたいディレクトリをweb.xmlに指定し、認証を行うサーブレットを書けば、該当するディレクトリに入っているサーブレットは自動的に認証処理を呼び出してくれます。
doFilter内で再度doFilterを呼び出していますが、前者はjavax.servlet.FilterインターフェイスのdoFilterで、後者はjavax.servlet.FilterChainインターフェイスのdoFilterであり、この両者は違うものです。
名前が同じわりに引数も違います。
FilterのdoFilterメソッドにはそこで行う処理を記述しますが、FilterChainのdoFilterメソッドは、次に当てはまる<filter>のdoFilterメソッドを実行します。
次に当てはまる<filter>が無ければ<servlet>を実行します。
よくわからなければとりあえずこう書くものだとだけわかっていればいいです。
最後に、企業や携帯サイトのように必ず上下にヘッダ、フッタを出力するフィルタを作成してみます。
headerFilter.java
public class headerFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { PrintWriter out = response.getWriter(); out.println("ヘッダ<hr>"); chain.doFilter(req, res); out.println("<hr>フッタ"); } public void init(FilterConfig config) throws ServletException{} public void destroy(){} } |
web.xml
<filter> <filter-name>charsetFilter</filter-name> <filter-class>charsetFilter</filter-class> </filter> <filter> <filter-name>headerFilter</filter-name> <filter-class>headerFilter</filter-class> </filter> <filter-mapping> <filter-name>charsetFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>headerFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
フィルタの実行順は、<filter-mapping>の順番です。
文字コードを設定するcharsetFilterサーブレットを先に書く必要があります。
前回までに作成した適当なサーブレットを呼び出してみると、当のサーブレットはまったくいじっていないのに、ヘッダとフッタが勝手に追加されているはずです。
doFilter部分が次のフィルタ、およびJavaサーブレットを実行している部分なので、それより下に処理を書くことでフッタ部分を設定することもできます。
このようにサーブレットを横断する処理を作成できるのが、Javaサーブレットの数少ない利点の一つです。
また、<url-pattern>に適合する限り、そのうち出てくるJSPにも適用されます。
これは便利な機能だ、と思いきやgetServletContext().getRequestDispatcher().forwardでJSPにフォワードした場合のみフィルタが適用されないという例によって意味のわからない仕様になっています。