HerokuでGrailsを使うとクエリパラメータが文字化けするのでbuildpack直しました。
TL;DR
HerokuではGrailsを簡単に(普通のGrailsプロジェクトをgit pushするだけで)デプロイすることができます。ですが、(少なくともHerokuがデフォルトで使う)Tomcat7ではクエリパラメータが文字化けします。Grailsで使うTomcat7のクエリパラメータをUTF-8でエンコードするようにするビルドパックをつくりました。(フォークして一行変えただけだけど)
問題
ローカルでGrailsを使って開発しているときには気づかなかったのですが、Herokuにデプロイするとクエリパラメータに日本語を使うとめっちゃ化けてしまいました。もうTomcatのことなんてすっかりわすれていたので、原因調査にめっちゃ時間かかりました。。。
それに付随していろいろわかったのでまとめておきます。
調査方針
次の方針で調査しました。結果としては最後の他の何かで、それはTomcatの設定だったんですけど、これをHerokuの場合にはどうするか?というのがHeroku初心者ながらにがんばった。
Viewの文字コード
Grails2.3.11でプロジェクトを新規作成すると、すでにConfig.groovyにUTF-8指定されています!
grails { views { gsp { encoding = 'UTF-8' htmlcodec = 'xml' // use xml escaping instead of HTML4 escaping codecs { expression = 'html' // escapes values inside ${} scriptlet = 'html' // escapes output from scriptlets in GSPs taglib = 'none' // escapes output from taglibs staticparts = 'none' // escapes output from static template parts } } // escapes all not-encoded output at final stage of outputting filteringCodecForContentType { //'text/html' = 'html' } } }
また、念のためgspのmetaタグにUTF-8を入れてみたけど変わりませんでした。
サーバーサイドでクエリパラメータをとってくるときにUTF-8になっていないのではないか
これもGrails2.3.11でプロジェクトを新規作成すると、既にConfig.groovyにUTF-8指定されています。
grails.converters.encoding = "UTF-8"
他のなにか
GrailsではクエリパラメータはContoller内でparamsというプロパティに格納されています。そこで次のような取得をすると文字化けのタイミングがわかりました。
- params → 文字化けしている
- request.getQueryString() → 文字化けしていない(requestから直接クエリーパラメータ部分の文字列を取得する)
paramsはGrailsParameterMapというクラスなのですが、コンストラクタでHttpServletRequestクラスのgetParameterMapメソッドから値を取得しています。具象クラスはこの場合は実際に利用しているサーバになってくるので、この場合はTomcatです。
ということで、Tomcatのクエリパラメータのエンコードをいじればよさそうです。
ここで、Tomcatの設定を変更する方法なのですが、組み込みTomcatの場合には次の方法がありました。
- org.grails.plugins.tomcat.ForkedTomcatCustomizerというクラスをつくる。 つくる場所はsrc/main/groovy配下になります。イメージとしてはこんな感じで、void customize(Tomcat tomcat) というメソッドを実装して設定を変更します。
package org.grails.plugins.tomcat import org.apache.catalina.connector.Connector import org.apache.catalina.startup.Tomcat class ForkedTomcatCustomizer{ void customize(Tomcat tomcat) { def c = new Connector(protocol: "HTTP/1.1", port: 8080, URIEncoding: "utf-8", redirectPort: 8443) tomcat.service.findConnectors().each {println "${it.protocol} ${it.URIEncoding}"} tomcat.service.addConnector(c) } }
前まではEvents.groovyで出来た気がしたのですが、どうもEvents.groovyに以前のようなTomcat用のイベントがこなくなったらしく、上記のような方法になったようです。
でも、私がやりたいのはHerokuのTomcatでした。
最初、この方法で組み込みTomcat以外の設定もオーバーライドされるのかと思ったのですが、そんな感じではなかったようです。で、ローカルにあるTomcatならserver.xmlを変更すればいいのですが、HerokuにあるTomcatを変更するとなると。。。って思い、buildpackを変更することにしました。
HerokuのGrailsビルドパックはJettyもしくはTomcatを利用するようになっていて、Tomcatの場合には別の場所からwebapp-runnerという別のリポジトリで管理されているtomcatをまるっとくるめたjarをダウンロードしてきて使っています。tomcatの設定を変更するにはこのwebapp-runnerをいじればよさそうです。
最新版である7.0.40.1をダウンロードしてきてヘルプを見てみます。
curl http://repo2.maven.org/maven2/com/github/jsimone/webapp-runner/7.0.40.1/webapp-runner-7.0.40.1.jar > webapp-runner.jar java -jar webapp-runner.jar --help
するとなんと--uri-encodigっていうオプションで指定できるよ!とか書いてあるじゃないですか!! やったね!
ということで、Grailsのbuildpackで指定しているRUNNER_OPTSという環境変数に--uri-encoding utf-8って指定をすればいいのですが、buildpack見てみると、ダウンロードしているwebapp-runner.jarが7.0.40.0で古くて、--uri-encodingに対応していない。。。
ということで、これをフォークして7.0.40.1のjarをダウンロードするようにしました。あとはフォークしたbuildpackを使えば問題ない感じです。
forkしたプロジェクトはこちら。(Pull Reqしたほうがいい気はしているが、フォークもとのブランチ戦略がよくわかっていなくっていま出していいのかよくわからん。
まとめ
2014/10/13現在で、Grails + Tomcatで出来るだけ簡単にHerokuにデプロイしてクエリパラメータを文字化けさせないためには次をする。
環境変数を追加する(コマンドで追加する場合)
heroku config:add BUILDPACK_URL=https://github.com/kyonmm/heroku-buildpack-grails heroku config:add RUNNER_OPTS=--uri-encoding utf-8
Githubのherokuボタンを利用しているなら、app.jsonにも同じように追加しましょう。
{ "env": { "BUILDPACK_URL": "https://github.com/kyonmm/heroku-buildpack-grails", "RUNNER_OPTS": "--uri-encoding utf-8", } }
補足
Jettyを使えばこんな問題とはおさらば出来ます!!ですが、Grailsのビルドパックで利用しているjetty-runnerの実装がイケていなくって、起動時にタイムアウトが頻発してしまいます(Herokuではアプリケーション起動に60秒以上かかってはいけない)。これは、環境変数PORTがjetty-runnerなかなか割り当てられないためのようです。これを解決するrunnerを書いている人もいるのですが、いまいち最新に追いついているのかわからなかったので今回は利用を見送りました。
そのうちJettyを使うならこうやるよ!っていうのを試してみたいです。
超補足
Heroku本買いました。わかりやすくて助かります。
プロフェッショナルのための 実践Heroku入門 プラットフォーム・クラウドを活用したアプリケーション開発と運用 (書籍)
- 作者: 相澤歩,arton,鳥井雪,織田敬子
- 出版社/メーカー: KADOKAWA/アスキー・メディアワークス
- 発売日: 2014/09/19
- メディア: 大型本
- この商品を含むブログ (2件) を見る
- 作者: Glen Smith,Peter Ledbrook
- 出版社/メーカー: Manning Pubns Co
- 発売日: 2014/07/24
- メディア: ペーパーバック
- この商品を含むブログを見る
- 作者: Burt Beckwith
- 出版社/メーカー: O'Reilly Media
- 発売日: 2013/04/23
- メディア: Kindle版
- この商品を含むブログを見る