先日、JBoss(ServletコンテナとしてTomcatを使っている)のクエリ文字列の文字コードの指定方法を調べていた時に、Tomcat 8のURIEncodingがISO-8859-1からUTF-8に変わっていたことを知りました。
たしかに・・・Tomcat 8にしてからURIEncodingを明示的に指定しなくても文字化けしなくなったような気はしていました。
せっかくなので、TomcatのURIエンコーディングについて簡単にまとめてみます。
Tomcat 7までのURIEncoding
Tomcat 7のドキュメントを見ると、ISO-8859-1と明記されています。
そのため、URI内のリクエストパラメータをUTF-8で解釈したい場合は、$TOMCAT_HOME/conf/server.xml
に以下のような定義を追加する必要がありました。
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8" /> <!-- ← 追加 -->
Tomcat 8からのURIEncoding
Tomcat 8のドキュメントを見ると、UTF-8と明記されています。
つまり、UTF-8にするために特になにもする必要はありません。(すばらしいですね)
いちおう、Tomcat 7(Servlet仕様?)と同じISO-8859-1にするオプションが用意されていて、システムプロパティ(-Dオプション)に「org.apache.catalina.STRICT_SERVLET_COMPLIANCE=true
」と指定すればよいみたいです。(たぶんこのオプションを使うことはないでしょう・・・)
Spring Boot組み込みTomcatのURIEncoding
Spring Boot上で組み込みTomcatを使用する場合は、Tomcatのバージョンに関係なくURIEncodingのデフォルトはUTF-8になります。
これは、Spring Bootが組み込みTomcatのコンフィギュレーションを行う際にUTF-8をデフォルト値として指定しているためです。
UTF-8以外の文字コードを使用したい場合は、application.yml
or application.properties
に文字コードを指定してください。
server:
tomcat:
uri-encoding: Windows-31J
useBodyEncodingForURI属性
TomcatにはURI内のクエリパラメータの文字コードを指定する方法として、URIEncoding
属性とは別にuseBodyEncodingForURI
属性を用意しています。
この属性をtrue
にすると、javax.servlet.ServletRequest#setCharacterEncoding(String)
で指定した文字コードを使用してクエリパラメータをデコードしてくれます。同一インスタンス内で複数の文字コードを扱う必要がある場合は、URIEncoding
属性ではなくuseBodyEncodingForURI
属性を使うことになると思います。(とはいえ、同一インスタンス内で複数の文字コードを扱わないように設計するのがよいとは思います)
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
useBodyEncodingForURI="true" /> <!-- ← 追加 -->
では、Spring Bootだとどうやって設定するのでしょうか?
Spring Boot 1.3.3.RELEASEの時点では、残念ながらyaml又はpropertiesファイルで指定する方法はありません。
このような場合は、Spring Bootが提供しているServlet Containerをカスタマイズする仕組み(EmbeddedServletContainerCustomizer
+ ConnectorCustomizer
)を使います。
@Bean
EmbeddedServletContainerCustomizer containerCustomizer() {
return (containerFactory) -> {
if (containerFactory instanceof TomcatEmbeddedServletContainerFactory) {
TomcatEmbeddedServletContainerFactory tomcatContainerFactory = (TomcatEmbeddedServletContainerFactory) containerFactory;
tomcatContainerFactory.addConnectorCustomizers((connector) -> {
connector.setUseBodyEncodingForURI(true);
});
}
};
}
SpringでServletRequest#setCharacterEncoding(String)する方法
ついでなので、Springアプリで文字コードを指定する方法も記載しておきます。
Spring MVCアプリで文字コード指定
Spring MVCを使う場合は、Spring Webから提供されているorg.springframework.web.filter.CharacterEncodingFilter
を使用して文字コードを指定します。
- XML based configuration
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- Java based configuration
public class SpringMvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// ...
@Override
protected Filter[] getServletFilters() {
return new Filter[]{new CharacterEncodingFilter("UTF-8")};
}
}
Spring Bootアプリで文字コード指定
Spring Boot 1.2からデフォルトでCharacterEncodingFilter
が適用されるようになっており、UTF-8が設定されます。(UTF-8でOKなら何もする必要はありません)
UTF-8以外の文字コードを使用したい場合は、application.yml
or application.properties
に文字コードを指定してください。
spring:
http:
encoding:
charset: Windows-31J
まとめ
最新のSpring Boot(1.3.3.RELEASE)を使えば、組み込みTomcatも8系だし文字コードがUTF-8のCharacterEncodingFilter
が自動で適用されるので、扱う文字コードがUTF-8なら何もしなくても文字化けしません!!
Spring Bootを使ってアプリケーションを作りましょう!!